home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 676-700 / 696 / ifslab / src / ifslab.c next >
C/C++ Source or Header  |  1995-03-18  |  119KB  |  3,134 lines

  1. /******************************************************************************\
  2. *                                                                              *
  3. *     IFS Lab, Copyright (C) 1992 by N. Zeldes. All rights reserved.           *
  4. *                                                                              *
  5. *                      Version 1.0, April 9, 1992                              *
  6. *                                                                              *
  7. *  An interactive program for exploring the IFS Code method of Fractal Image   *
  8. *  representation and reconstruction.                                          *
  9. *                                                                              *
  10. *  Requires the Fox/Dawson freeware req.library in libs: directory.            *
  11. *                                                                              *
  12. *  Compile under Aztec C 5.0, with -ff option (Fast floating point)            *
  13. *   and -safmnpsu options (code optimization).                                 *
  14. *  Link with +cd option (forces sprite and image data to load in CHIP RAM).    *
  15. *  Link with IFF files (from CBM IFF Disk, Fish disk #185), and with the glue  *
  16. *  module for the Fox/Dawson requester library, thus:                          *
  17. *                                                                              *
  18. *  cc  -ff -safmnpsu -i IFF -i req IFSLab.c                                    *
  19. *  ln  +cd IFSLab.o IFF/putpict.o IFF/ilbmw.o IFF/packer.o                     *
  20. *    IFF/iffw.o IFF/readpict.o IFF/ilbmr.o IFF/unpacker.o IFF/iffr.o           *
  21. *    IFF/remalloc.o req/myreqglue.o  mf.lib c.lib                              *
  22. *                                                                              *
  23. \******************************************************************************/
  24.  
  25. #include <intuition/intuition.h>
  26. #include <exec/memory.h> 
  27.         /* exec/memory.h required to use AllocMem() */
  28. #include <graphics/text.h>
  29.         /* graphics/text.h required for font TextAttr structure */
  30. #include <stdio.h>
  31. #include <math.h>    /* Floating point math: sqrt, fabs, trig functions */
  32. #include <errno.h>   /* Required by myatan2() */
  33. #include <graphics/display.h> /* needed for definition of INTERLACE */
  34. #include <stdlib.h>   /* stdlib.h needed for rand() */
  35. #include <time.h>     /* time.h needed for time() */
  36. #include <string.h>   /* needed for memset */
  37.  
  38. #include <ilbm.h>              /* Header file from IFF Disk (Note that   */
  39.                                /* ilbm.h #includes compiler.h and iff.h) */
  40. #include <readpict.h>          /* From IFF Disk */
  41. #include <remalloc.h>          /* From IFF Disk; For ChipAlloc(), RemFree() */
  42. #include <libraries/dos.h>     /* For IFF file I/O stuff */
  43. #include <libraries/dosextens.h> /* For the Process structure definition. */
  44. #include <reqbase.h>             /* Fox/Dawson file requester header file */
  45. #include "IFSLab.h"   /* PowerWindows-generated structures and defines */
  46.  
  47. #define INTUITION_REV 31L
  48. #define GRAPHICS_REV  31L
  49.  
  50. #define OUTLINE  0    /* parameters for SetMode() */
  51. #define COLLAGE  1
  52.  
  53. #define FREEHAND 0    /* drawmode values */
  54. #define LINES    1
  55. #define ERASE    2
  56. #define FILL     3
  57. #define VECTOR   4
  58.  
  59. #define CODES      0  /* parameters for AboutText() */
  60. #define IFS_THEORY 1
  61. #define HELP1      2
  62. #define HELP2      3
  63. #define AUTHOR     4
  64. #define DEMOTEXT   5
  65.  
  66. #define WIDTH   Window->Width
  67. #define HEIGHT  Window->Height
  68. #define GZZWIDTH   Window->GZZWidth
  69. #define GZZHEIGHT  Window->GZZHeight
  70.  
  71. #define MAX_N 19      /* max permitted piece index */
  72.  
  73. struct Screen *Screen = NULL, *ImageScreen;
  74. struct RastPort *scrp;     /* Pointer to Screen RastPort */
  75. struct Window *Window = NULL, *NumWindow;
  76. struct RastPort *r;  /* Pointer to RastPort of main editing window */
  77. struct Window *ImageWindow;  /* Window for rendering Image */
  78. struct RastPort *Textrp;   /* Rastport of Text Window */
  79. struct IntuitionBase *IntuitionBase = NULL;
  80. struct GfxBase *GfxBase = NULL;
  81. void *MathBase = NULL;
  82. void *MathTransBase = NULL;
  83. struct ReqLib *ReqBase = NULL;     /* Fox/Dawson Requester Library */
  84. int drawmode = FREEHAND;   
  85.   /* Current mode - FREEHAND,LINES,ERASE,FILL (outline) or VECTOR (Collage) */
  86. int exists_outline = 0, exists_image = 0, exists_numwindow = 0;
  87. int renderwidth = 640, renderheight = 400; /* initialize to default */
  88. int renderdepth = 1;  /* initialize to default */
  89. long mx, my;  /* Current Mouse coordinates in editing */
  90.  
  91. struct pixel {
  92.   long x;
  93.   long y;
  94. };
  95. struct piece {
  96.   double a, b, c, d, e, f;
  97.   double s1, s2, r1, r2;
  98.   double dens;
  99.   double p;
  100.   double det;
  101.   UBYTE *piecemap;
  102.   struct pixel boxo, boxx, boxy, boxz;
  103. };
  104. struct piece *pieceptr[MAX_N + 1]; /* Array of pointers to piece structures */
  105. int N = -1;   /* Index of highest existing Piece Initialized to zero pieces */
  106. int selpiece; /* Index of currently selected Piece */
  107. char curcor; /* Currently dragged Box corner ('o','x','y','z') or none ('0') */
  108. struct pixel ghboxo, ghboxx, ghboxy, ghboxz; /* Current corners of ghost Box */
  109. struct pixel tmpboxo, tmpboxx, tmpboxy, tmpboxz;
  110. struct piece *AllocPiece();
  111. double myatan2();  /* My version of atan2() which is missing in Manx mf.lib */
  112. char *fname, *GetFileName();
  113. struct Library *OpenLibrary();
  114. void *AllocMem(), *AllocRaster();
  115. UBYTE *outlinebufptr=NULL; /* ptr to buffer holding outline in collage mode */
  116. long i;      /* handy as a loop index */
  117.  
  118. /*** IFF error messages (external) */
  119.  
  120. char MsgOkay[] = { "(IFF_OKAY) A good IFF file" };
  121. char MsgEndMark[] = { "(END_MARK) IFF Loader Error" };
  122. char MsgDone[] = { "(IFF_DONE) Well Done!" };
  123. char MsgDos[] = { "DOS Error - Aborting Load!" };
  124. char MsgNot[] = { "Not an IFF file - Aborting Load!" };
  125. char MsgNoFile[] = { "No such file found - Aborting Load!" };
  126. char MsgClientError[] = { "(CLIENT_ERROR) IFF Checker bug"};
  127. char MsgForm[] = { "(BAD_FORM) IFF Loader Error" };
  128. char MsgShort[] = { "(SHORT_CHUNK) IFF Loader Error" };
  129. char MsgBad[] = { "A mangled IFF file - Aborting Load!" };
  130.  
  131. /* MUST GET THESE IN RIGHT ORDER!!*/
  132. char *IFFPMessages[-LAST_ERROR+1] = {
  133.     /*IFF_OKAY*/  MsgOkay,
  134.     /*END_MARK*/  MsgEndMark,
  135.     /*IFF_DONE*/  MsgDone,
  136.     /*DOS_ERROR*/ MsgDos,
  137.     /*NOT_IFF*/   MsgNot,
  138.     /*NO_FILE*/   MsgNoFile,
  139.     /*CLIENT_ERROR*/ MsgClientError,
  140.     /*BAD_FORM*/  MsgForm,
  141.     /*SHORT_CHUNK*/  MsgShort,
  142.     /*BAD_IFF*/   MsgBad
  143.     };
  144.  
  145. struct FileRequester MyFileReqStruct;    /* File Requester stuff */
  146. char filename[FCHARS+1];
  147. char directoryname[DSIZE+1];
  148. char pathname[FCHARS+DSIZE+2+4];  /* +4 to permit space for appending '.ifs' */
  149.  
  150. /*==========================================================================*/
  151.  
  152. main()
  153. {
  154.   struct Message * GetMsg();
  155.   void ReplyMsg();
  156.   struct IntuiMessage *message;
  157.   ULONG class;
  158.   USHORT code, MenuNumber, menuid, itemid, subitemid;
  159.   struct MenuItem *ItemAddress(), *ItemAddr;
  160.   long labs();
  161.   int w;  /* temporary int variable */
  162.   int hitgadget;  /* GadgetID # of last gadget hit by mouse */
  163.   struct Gadget *hitgadstruct; /* Gadget Structure of last hit gadget */
  164.   int numgadgpending;  /* GadgetID of unserviced coeff gadget, or -1 if none */
  165.   struct piece tmppiece;  /* will be used to hold temporary trans' coeffs */
  166.   long mx0,my0, mx1,my1;  /* coords of endpoints of ghost line in Lines mode */
  167.   int pendown = 0;
  168.              /* 0 if mouse SELECT button is currently pressed in window */
  169.   SHORT WindowBdrArray[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
  170.              /* will be filled later to corners of inner GZZ Window */
  171.   struct Gadget *gad;  /* used as a temporary variable */
  172.  
  173.   /**** Initialize stuff, set up edit window in custom screen etc. ****/
  174.  
  175.   OpenLibraries();
  176.   OpenDisplay(); /* Open Outline Sketch  window in custom screen */
  177.  
  178.   SetAPen(r, 2L);
  179.   WindowBdrArray[2] = WindowBdrArray[4] = GZZWIDTH - 1;
  180.   WindowBdrArray[5] = WindowBdrArray[7] = GZZHEIGHT - 1;
  181.  
  182.   /*** Allocate memory for Outline buffer bitmap */
  183.   if ((outlinebufptr = 
  184.          (UBYTE *)AllocMem((long)RASSIZE(WIDTH,HEIGHT), MEMF_CHIP)) == NULL) {
  185.     ShowError("Can't allocate Outline Buffer Memory!");
  186.     CloseAll();
  187.   }
  188.        /**** Main Loop ****/
  189.  
  190.   while(1) {
  191.  
  192.       mx = (long)Window->GZZMouseX;
  193.       my = (long)Window->GZZMouseY;
  194.  
  195.     if (pendown == 1) {
  196.  
  197.      /*** Do Mouse editing to board per drawmode and mouse coords */
  198.       switch (drawmode) {
  199.  
  200.       case FREEHAND:
  201.         Draw(r, mx, my); 
  202.         break;
  203.  
  204.       case LINES:
  205.         SetDrMd(r, JAM1|COMPLEMENT);  /* COMPLEMENT used for ghost line */
  206.         Move(r, mx0, my0);
  207.         Draw(r, mx1,my1);   /* Undraw previous ghost line */
  208.         Move(r, mx0, my0);
  209.         Draw(r, mx1 = mx, my1 = my);  /* Draw a new ghost line */
  210.         SetDrMd(r, JAM1);
  211.         break;
  212.  
  213.       case ERASE:
  214.         RectFill(r, mx - 2, my - 2, mx + 2, my + 2);
  215.         break;
  216.  
  217.       case FILL:
  218.         break;
  219.  
  220.       case VECTOR:    /* Change the Ghost Vector Box */
  221.         if (exists_numwindow || curcor == '0')
  222.           break;
  223.  
  224.         /* Check for attempt to size box to a point */
  225.         if ( (mx==pieceptr[selpiece]->boxo.x && my==pieceptr[selpiece]->boxo.y)
  226.          &&  ( (curcor == 'z') || 
  227.                (curcor == 'x' && 
  228.                 pieceptr[selpiece]->boxy.x == pieceptr[selpiece]->boxo.x &&
  229.                 pieceptr[selpiece]->boxy.y == pieceptr[selpiece]->boxo.y) ||
  230.                (curcor == 'y' && 
  231.                 pieceptr[selpiece]->boxx.x == pieceptr[selpiece]->boxo.x &&
  232.                 pieceptr[selpiece]->boxx.y == pieceptr[selpiece]->boxo.y)
  233.               )
  234.         )
  235.           mx = (mx == 0) ? 1 : mx - 1; /* If attempted, tweak mx to avoid */
  236.  
  237.         ComputeNewBox();       /* Compute New Ghost Box from mouse coords */
  238.         ToggleGhostBox();      /* Undraw old ghost box */
  239.         ghboxo = tmpboxo;      /* Update ghost box variables */
  240.         ghboxx = tmpboxx;
  241.         ghboxy = tmpboxy;
  242.         ghboxz = tmpboxz;
  243.         ToggleGhostBox();      /* Draw new ghost box */
  244.         break;
  245.       }    /* End Switch */
  246.     }    /* end if pendown == 1 */
  247.  
  248.     /* Get an IDCMP event, if any, from Window */
  249.     if (message = (struct IntuiMessage *)GetMsg(Window->UserPort)) { 
  250.       class = message->Class;
  251.       code = message->Code;
  252.       ReplyMsg(message);
  253.     }
  254.     else  /* no message */
  255.       class = code = NULL; /* so event routines below will be skipped */
  256.  
  257.     /*** Go case by case over possible events from port */
  258.  
  259.     if (class == MOUSEBUTTONS) {    /*** Respond to user mousebutton clicks */
  260.       if (code == SELECTDOWN) {
  261.         pendown = 1;
  262.         if (drawmode != ERASE)
  263.           exists_outline = 1;
  264.         switch (drawmode) {
  265.  
  266.         case FREEHAND:
  267.         case ERASE:
  268.           Move(r, mx, my);
  269.           break;
  270.  
  271.         case LINES:
  272.           mx0 = mx1 = mx; /* Attach both ends of line to pixel */
  273.           my0 = my1 = my;
  274.           break;
  275.  
  276.         case FILL:
  277.           ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  278.           SetAPen(r, 2L);
  279.           Move(r, 0L, 0L);
  280.           PolyDraw(r,5L,WindowBdrArray); /* temporary border to prevent */
  281.           Flood(r, 0L, mx, my);          /* messes on flood fill to edge */
  282.           SetAPen(r, 0L);
  283.           Move(r, 0L, 0L);
  284.           PolyDraw(r,5L,WindowBdrArray);
  285.           ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  286.           break;
  287.  
  288.         case VECTOR:
  289.           if (exists_numwindow)
  290.             break;
  291.           /*** Decide which corner, if any, the user clicked on */
  292.           curcor = '0';
  293.           w = selpiece;    /* Just to keep source lines shorter... */
  294.           if (labs(mx-pieceptr[w]->boxo.x)<5&&labs(my-pieceptr[w]->boxo.y)<5)
  295.             curcor = 'o';
  296.           if (labs(mx-pieceptr[w]->boxx.x)<5&&labs(my-pieceptr[w]->boxx.y)<5)
  297.             curcor = 'x';
  298.           if (labs(mx-pieceptr[w]->boxy.x)<5&&labs(my-pieceptr[w]->boxy.y)<5)
  299.             curcor = 'y';
  300.           if (labs(mx-pieceptr[w]->boxz.x)<5&&labs(my-pieceptr[w]->boxz.y)<5)
  301.             curcor = 'z';
  302.           if (curcor != '0') {     /* Set up ghost box for starters */
  303.  
  304.             /*** Update ghost box variables by calling ComputeNewBox() */
  305.             ComputeNewBox();
  306.             ghboxo = tmpboxo;               /* Structure Assignments */
  307.             ghboxx = tmpboxx;
  308.             ghboxy = tmpboxy;
  309.             ghboxz = tmpboxz;
  310.  
  311.             ToggleGhostBox();  /* Draw initial ghost box */
  312.           }
  313.           break;
  314.         }            /* End switch */
  315.       }       /* End if SELECTDOWN */
  316.  
  317.       else if (code == SELECTUP) {
  318.         pendown = 0;
  319.         switch (drawmode) {
  320.  
  321.         case FREEHAND:
  322.         case ERASE:
  323.         case FILL:
  324.           break;
  325.  
  326.         case LINES:
  327.           SetAPen(r, 2L);   /* Draw final line (Jam onto last Ghost line) */
  328.           Move(r,mx0, my0);
  329.           Draw(r, mx1, my1);
  330.           break;
  331.  
  332.         case VECTOR:
  333.           if (exists_numwindow || curcor == '0')
  334.             break;
  335.           ToggleGhostBox();  /* Undraw Ghost Box */
  336.           /*** Compute all coeffs from ghbox corners, put into tmppiece */
  337.           tmppiece = *pieceptr[selpiece];  /* Structure assignment */
  338.           tmppiece.s1 = 
  339.            sqrt((double)((ghboxx.x-ghboxo.x)*(ghboxx.x-ghboxo.x) +
  340.             (ghboxx.y-ghboxo.y)*(ghboxx.y-ghboxo.y)))/GZZWIDTH;
  341.           tmppiece.s2 = 
  342.            sqrt((double)((ghboxy.x-ghboxo.x)*(ghboxy.x-ghboxo.x) +
  343.             (ghboxy.y-ghboxo.y)*(ghboxy.y-ghboxo.y)))/GZZHEIGHT;
  344.           tmppiece.r1 = myatan2((double)(ghboxx.y-ghboxo.y),
  345.            (double)(ghboxx.x-ghboxo.x));
  346.           tmppiece.r2 = myatan2((double)(ghboxo.x-ghboxy.x),
  347.            (double)(ghboxy.y-ghboxo.y));
  348.           tmppiece.a = tmppiece.s1*cos(tmppiece.r1);
  349.           tmppiece.b = -tmppiece.s2*sin(tmppiece.r2);
  350.           tmppiece.c = tmppiece.s1*sin(tmppiece.r1);
  351.           tmppiece.d = tmppiece.s2*cos(tmppiece.r2);
  352.           tmppiece.e = (double)(ghboxo.x)/GZZWIDTH;
  353.           tmppiece.f = (double)(ghboxo.y)/GZZWIDTH;
  354.  
  355.           /*** Erase Plane 3 before transformation delay */
  356.           SetWrMsk(r,0xFFF8);  /* Write-protect planes 0,1,2 */
  357.           SetRast(r, 0L);
  358.           SetWrMsk(r,0xFFFF);
  359.           /* Transform the selected piece per user drag of vector box */
  360.           TransformPiece(outlinebufptr,
  361.                                    pieceptr[selpiece]->piecemap, &tmppiece);
  362.            /*** Erase Plane 2, then Blit Selected Piece from piecemap to it */
  363.           SetWrMsk(r,0xFFF4);  /* Write-protect planes 0,1,3 */
  364.           SetRast(r, 0L);
  365.           SetAPen(r, 4L);
  366.           BltTemplate((char*)pieceptr[selpiece]->piecemap,
  367.            (long)Window->BorderLeft,(long)WIDTH/8, r, 0L, 0L,
  368.              (long)GZZWIDTH, (long)GZZHEIGHT);
  369.           /* Rastport WrMask will be restored in DrawBox() called below */
  370.  
  371.           /*** Recompute probabilities and update piece structures */
  372.           tmppiece.boxo = ghboxo;  /* structure assignment */
  373.           tmppiece.boxx = ghboxx;  /* structure assignment */
  374.           tmppiece.boxy = ghboxy;  /* structure assignment */
  375.           tmppiece.boxz = ghboxz;  /* structure assignment */
  376.           *pieceptr[selpiece] = tmppiece;  /* structure assignment */
  377.           pieceptr[selpiece]->det = 
  378.            fabs(tmppiece.a * tmppiece.d - tmppiece.b * tmppiece.c);
  379.           if (pieceptr[selpiece]->det == 0.0)
  380.             pieceptr[selpiece]->det = 0.01;
  381.           ComputeProbs();      /* Adjust all piece probabilities */
  382.           DrawBox(); /* Redraw Vector Box for modified selected piece */
  383.           curcor = '0';
  384.           break;
  385.         }            /* End switch */
  386.       }              /* End else if SELECTUP */
  387.     }                /* End if MOUSEBUTTONS */
  388.  
  389.     else if (class == GADGETUP) {   /*** Respond to Gadget clicked in Window */
  390.  
  391.       hitgadget = ((struct Gadget *)(message->IAddress))->GadgetID;
  392.                                    /* ID# of just-selected gadget structure */
  393.       if (hitgadget <= 3)    /* a draw mode gadget in Outline mode */
  394.         HiliteGadget(hitgadget);
  395.       switch(hitgadget) {
  396.  
  397.       case 0:
  398.         drawmode = FREEHAND;
  399.         SetAPen(r,2L);
  400.         break;
  401.  
  402.       case 1:
  403.         drawmode = LINES;
  404.         SetAPen(r,2L);
  405.         break;
  406.  
  407.       case 2:
  408.         drawmode = ERASE;
  409.         SetAPen(r, 0L);
  410.         SetOPen(r, 0L);
  411.         break;
  412.  
  413.       case 3:
  414.         drawmode = FILL;
  415.         SetOPen(r, 2L);   /* (A Pen defined before doing the flood fill) */
  416.         break;
  417.  
  418.       case 4:             /* CLROUT (Clear Outline) gadget */
  419.         SetRast(r, 0L);
  420.         exists_outline = 0;
  421.         if(drawmode == ERASE || drawmode == FILL) {
  422.           drawmode = FREEHAND;
  423.           SetAPen(r,2L);
  424.           HiliteGadget(0);
  425.         }
  426.         break;
  427.  
  428.       case 5:             /* DONE gadget */
  429.         SetMode(COLLAGE);
  430.         AddDefPiece();
  431.         DrawCollage();
  432.         break;
  433.  
  434.       case 6:             /* ADD gadget */
  435.         AddDefPiece();
  436.         DrawCollage();
  437.         if (exists_numwindow)
  438.           UpdateNumStrings();
  439.         break;
  440.  
  441.       case 7:             /* DUP gadget */
  442.         DupSelPiece();
  443.         DrawCollage();
  444.         if (exists_numwindow)
  445.           UpdateNumStrings();
  446.         break;
  447.  
  448.       case 8:             /* SELECT gadget */
  449.         if (--selpiece < 0)
  450.           selpiece = N;
  451.         DrawCollage();
  452.         if (exists_numwindow)
  453.           UpdateNumStrings();
  454.         break;
  455.  
  456.       case 9:             /* DELETE gadget */
  457.         DeleteSelPiece();
  458.         DrawCollage();
  459.         ComputeProbs();
  460.         if (exists_numwindow)
  461.           UpdateNumStrings();
  462.         break;
  463.  
  464.       case 10:            /* CLRIFS gadget */
  465.         ClearIFS();
  466.         AddDefPiece();
  467.         DrawCollage();
  468.         if (exists_numwindow)
  469.           UpdateNumStrings();
  470.         break;
  471.  
  472.       case 11:            /* NUMBERS Gadget */
  473.         if (!exists_numwindow) {
  474.           UpdateNumStrings();
  475.           OpenNumWindow();
  476.           numgadgpending = -1;
  477.         }
  478.  
  479.         break;
  480.       }   /* end switch hitgadget */
  481.     }     /* End else if GADGETUP */
  482.  
  483.     else if (class == MENUPICK) {     /*** Respond to menu selections */
  484.  
  485.       MenuNumber = code;         /* (First) item selection returned by IDCMP */
  486.       while (MenuNumber != MENUNULL) { /*** service multiply chosen items */
  487.                                        /* (if any) */
  488.         ItemAddr = ItemAddress(&MenuList1, (long)MenuNumber);
  489.        
  490.         menuid = MENUNUM(MenuNumber); /* Extract menu, item from IDCMP code */
  491.         itemid = ITEMNUM(MenuNumber);
  492.         subitemid = SUBNUM(MenuNumber);
  493.  
  494.         if (menuid == 0) {   /* PROJECT Menu */
  495.           switch (itemid) {
  496.  
  497.           case 0:            /* NEW */
  498.             if (SetMode(OUTLINE) == 1) {
  499.               SetRast(r, 0L);
  500.               exists_outline = 0;
  501.               if (exists_image)
  502.                 CloseImageScreen();
  503.             }
  504.             break;
  505.  
  506.           case 1:            /* EDIT OUTLINE */
  507.             SetMode(OUTLINE);
  508.             break;
  509.  
  510.           case 2:            /* LOAD FILE (with subitems) */
  511.             if(ReqBase == NULL) {    /* req.library missing - no file I/O */
  512.               ShowError("req.library not found!");
  513.               break;
  514.             }
  515.             fname = GetFileName("File to Load:");
  516.             if (*fname != '\0') {
  517.  
  518.               switch (subitemid) {
  519.  
  520.               case 0:    /* Load Outline */
  521.                 if (SetMode(OUTLINE) == 1)
  522.                   LoadILBM(fname, 0);
  523.                 break;
  524.  
  525.               case 1:    /* Load IFS */
  526.                 SetMode(COLLAGE);
  527.                 if (LoadIFS(fname) == NULL) {  /* If load failed */
  528.                   ClearIFS();
  529.                   AddDefPiece();
  530.                   DrawCollage();
  531.                 }
  532.                 break;
  533.  
  534.               case 2:    /* Load Image */
  535.                 LoadILBM(fname, 1);
  536.                 break;
  537.               }    /* end switch subitemid */
  538.             }      /* end if *fname etc */
  539.             break;
  540.  
  541.           case 3:            /* SAVE FILE (with subitems) */
  542.             if(ReqBase == NULL) {    /* req.library missing - no file I/O */
  543.               ShowError("req.library not found!");
  544.               break;
  545.             }
  546.             switch (subitemid) {
  547.  
  548.             case 0:    /* Save IFS */
  549.               SaveIFS();
  550.               break;
  551.  
  552.             case 1:    /* Save Image */
  553.               SaveILBM();
  554.               break;
  555.             }    /* end switch subitemid */
  556.             break;
  557.  
  558.           case 4:            /* OPTIMIZE */
  559.             if (drawmode != VECTOR)
  560.               ShowError("No IFS to Optimize!");
  561.             else
  562.               Optimize(1);
  563.             break;
  564.  
  565.           case 5:            /* QUIT */
  566.             CloseAll();
  567.             break;
  568.           }   /* end switch itemid */
  569.         }     /* end if menuid == 0) */
  570.  
  571.         else if (menuid == 1) {   /* DISPLAY Menu */
  572.           switch (itemid) {
  573.  
  574.           case 0:            /* SHOW CODES */
  575.             if (drawmode != VECTOR)
  576.               ShowError("No IFS!");
  577.             else
  578.               AboutText(CODES); 
  579.             break;
  580.  
  581.           case 1:            /* SHOW IMAGE */
  582.             if (exists_image) {
  583.               MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge));
  584.               ScreenToFront(ImageScreen);
  585.             }
  586.             else
  587.               ShowError("No Image!");
  588.             break;
  589.           }   /* end switch itemid */
  590.         }     /* end else if menuid == 1) */
  591.  
  592.         else if (menuid == 2) {   /* RENDER Menu */
  593.           switch (itemid) {
  594.  
  595.           case 0:            /* RESOLUTION (with subitems) */
  596.             renderwidth = (subitemid > 0) ? 640 : 320;
  597.             renderheight = (subitemid > 1) ? 400 : 200;
  598.             break;
  599.  
  600.           case 1:            /* GRAY LEVELS (with subitems) */
  601.             renderdepth = subitemid + 1;
  602.             break;
  603.  
  604.           case 2:            /* START */
  605.             while(message = (struct IntuiMessage *)GetMsg(Window->UserPort)) 
  606.               ReplyMsg(message);  /* Clean out IDCMP port before closing it */
  607.             ModifyIDCMP(Window, NULL);   /* Disable IDCMP during rendering */
  608.             ClearMenuStrip(Window); /* remove main window menus during render*/
  609.             RenderImage(0);     /* render the attractor in Image Screen */
  610.             SetMenuStrip(Window, &MenuList1);
  611.             ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK);
  612.             break;
  613.           }   /* end switch itemid */
  614.         }     /* end else if menuid == 2) */
  615.  
  616.         else if (menuid == 3) {   /* ABOUT Menu */
  617.           switch (itemid) {
  618.  
  619.           case 0:            /* IFS THEORY */
  620.             AboutText(IFS_THEORY);
  621.             break;
  622.  
  623.           case 1:            /* HELP */
  624.             AboutText(HELP1);
  625.             AboutText(HELP2);
  626.             break;
  627.  
  628.           case 2:            /* AUTHOR */
  629.             AboutText(AUTHOR);
  630.             break;
  631.  
  632.           case 3:            /* DEMO */
  633.             DoDemo();
  634.             break;
  635.           }   /* end switch itemid */
  636.         }     /* end else if menuid == 3) */
  637.  
  638.         MenuNumber = ItemAddr->NextSelect; 
  639.                       /* Loop back for next multiply selected item, if any */       
  640.       }   /* end while MenuNumber != MENUNULL */
  641.     }     /* end else if MENUPICK */
  642.  
  643.     /*** Get & serve an IDCMP event, if any, from Coefficients Window */
  644.  
  645.     if (exists_numwindow) {
  646.       if (message = (struct IntuiMessage *)GetMsg(NumWindow->UserPort)) { 
  647.         ReplyMsg(message);
  648.  
  649.         /* Note that all events in this window are GADGETUP, GADGETDOWN */
  650.         /* or CLOSEWINDOW */
  651.  
  652.         if (message->Class == CLOSEWINDOW) {
  653.           CloseWindow(NumWindow);
  654.           exists_numwindow = 0;
  655.           numgadgpending = -1;
  656.           SetMenuStrip(Window, &MenuList1);
  657.         }
  658.         else if (message->Class == GADGETDOWN) {  /* ENTER or string gadget */
  659.           if (numgadgpending != -1)
  660.             RecomputeCoeffs(numgadgpending);
  661.           hitgadstruct = (struct Gadget *)(message->IAddress);
  662.                                                /* pointer to just-hit gadget */
  663.           numgadgpending = hitgadstruct->GadgetID;
  664.                                    /* ID# of just-selected gadget structure */
  665.  
  666.           /**** Refresh all string gadgets except just-hit one  */
  667.           for(gad = &NumAcoeffGadg; gad != NULL; gad = gad->NextGadget)
  668.             if (gad->GadgetID != numgadgpending)
  669.               RefreshGList(gad, NumWindow, NULL, 1L);
  670.           if (numgadgpending == 12)
  671.             numgadgpending = -1;     /* It was only the ENTER Gadget */
  672.         }
  673.         else if (message->Class == GADGETUP) {
  674.           hitgadget = ((struct Gadget *)(message->IAddress))->GadgetID;
  675.                                    /* ID# of just-selected gadget structure */
  676.           switch (hitgadget) {
  677.  
  678.           case 12:       /* ENTER */
  679.  
  680.             tmppiece = *pieceptr[selpiece];  /* Structure assignment */
  681.  
  682.             /* Copy all coeffs from string gadgets to tmppiece */
  683.  
  684.             sscanf((char*)NumNumR2coeffGadgSIBuff, "%lf", &tmppiece.r2);
  685.             sscanf((char*)NumNumR1coeffGadgSIBuff, "%lf", &tmppiece.r1);
  686.             sscanf((char*)NumNumS2coeffGadgSIBuff, "%lf", &tmppiece.s2);
  687.             sscanf((char*)NumNumS1coeffGadgSIBuff, "%lf", &tmppiece.s1);
  688.             sscanf((char*)NumNumPcoeffGadgSIBuff, "%lf", &tmppiece.p);
  689.             sscanf((char*)NumNumDenscoeffGadgSIBuff, "%lf",
  690.                                                    &tmppiece.dens);
  691.             sscanf((char*)NumNumFcoeffGadgSIBuff, "%lf", &tmppiece.f);
  692.             sscanf((char*)NumNumEcoeffGadgSIBuff, "%lf", &tmppiece.e);
  693.             sscanf((char*)NumNumDcoeffGadgSIBuff, "%lf", &tmppiece.d);
  694.             sscanf((char*)NumNumCcoeffGadgSIBuff, "%lf", &tmppiece.c);
  695.             sscanf((char*)NumNumBcoeffGadgSIBuff, "%lf", &tmppiece.b);
  696.             sscanf((char*)NumNumAcoeffGadgSIBuff, "%lf", &tmppiece.a);
  697.  
  698.             tmppiece.r1 = tmppiece.r1*0.017453293; /* convert degs to radians*/
  699.             tmppiece.r2 = tmppiece.r2*0.017453293;
  700.  
  701.             /*** Erase Plane 3 before transformation delay */
  702.             SetWrMsk(r,0xFFF8);  /* Write-protect planes 0,1,2 */
  703.             SetRast(r, 0L);
  704.             SetWrMsk(r, 0xFFFF);
  705.  
  706.             /* Transform the selected piece per user-entered coefficients */
  707.             TransformPiece(outlinebufptr,
  708.                                pieceptr[selpiece]->piecemap, &tmppiece);
  709.             /*** Erase Plane 2, then Blit Selected Piece from piecemap to it */
  710.             SetWrMsk(r,0xFFF4);  /* Write-protect planes 0,1,3 */
  711.             SetRast(r, 0L);
  712.             SetAPen(r, 4L);
  713.             BltTemplate((char*)pieceptr[selpiece]->piecemap,
  714.              (long)Window->BorderLeft,(long)WIDTH/8, r, 0L, 0L,
  715.               (long)GZZWIDTH, (long)GZZHEIGHT);
  716.             /* Rastport WrMask will be restored in DrawBox() called below */
  717.  
  718.             /*** Compute new Box corners from new coeffs */
  719.             tmppiece.boxo.x = tmppiece.e * GZZWIDTH;
  720.             tmppiece.boxo.y = tmppiece.f * GZZWIDTH; /* sic! */
  721.             tmppiece.boxx.x = (tmppiece.a + tmppiece.e) * GZZWIDTH;
  722.             tmppiece.boxx.y = (tmppiece.c + tmppiece.f) * GZZWIDTH;
  723.             tmppiece.boxy.x = (tmppiece.b*GZZHEIGHT/GZZWIDTH + tmppiece.e) *
  724.                                GZZWIDTH;
  725.             tmppiece.boxy.y = (tmppiece.d*GZZHEIGHT/GZZWIDTH + tmppiece.f) *
  726.                                GZZWIDTH;
  727.             tmppiece.boxz.x = 
  728.                        tmppiece.boxy.x + tmppiece.boxx.x - tmppiece.boxo.x;
  729.             tmppiece.boxz.y = 
  730.                        tmppiece.boxy.y + tmppiece.boxx.y - tmppiece.boxo.y;
  731.  
  732.             *pieceptr[selpiece] = tmppiece;  /* structure assignment */
  733.             /*** Recompute probabilities and update piece structures */
  734.             pieceptr[selpiece]->det = 
  735.              fabs(tmppiece.a * tmppiece.d - tmppiece.b * tmppiece.c);
  736.             if (pieceptr[selpiece]->det == 0.0)
  737.               pieceptr[selpiece]->det = 0.01;
  738.             ComputeProbs();      /* Adjust all piece probabilities */
  739.             DrawBox(); /* Redraw Vector Box for modified selected piece */
  740.             break;
  741.  
  742.           default:       /* GADGETUP on any coefficient string gadget */              
  743.             numgadgpending = -1;
  744.             RecomputeCoeffs(hitgadget);
  745.             RefreshGadgets(&NumGadgetList3, NumWindow, NULL);
  746.             break;
  747.           }  /* end switch hitgadget */
  748.         }    /* end else if ... GADGETUP */
  749.       }      /* end if message etc of coeffs window */
  750.     }        /* end if exists_numwindow */
  751.  
  752.     /*** Get & serve an IDCMP event, if any, from Image Window */
  753.     /*   Note that all events in this window are MOUSEBUTTONS  */
  754.  
  755.     if (exists_image) {
  756.       if (message = (struct IntuiMessage *)GetMsg(ImageWindow->UserPort)) { 
  757.         ReplyMsg(message);
  758.         if (message->Code == SELECTUP) { /* User clicked - push Image to back*/
  759.           ScreenToBack(ImageScreen);
  760.           MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge));
  761.           ScreenToFront(Screen);
  762.           ActivateWindow(Window);
  763.         }
  764.       }
  765.     }        /* End if exists_image */
  766.   }          /* End while of main loop */
  767. }            /* End main() */
  768. /*=========================================================================*/
  769.  
  770. char * GetFileName(prompt)    /* Returns pointer to a filename selected from */
  771. char *prompt;        /* a file requester. Prompt appears at top of requester */
  772.                      /* window. If user selected CANCEL,returns a nullstring */
  773.                      /* Places the requester in a custom 640x200 screen.     */
  774.  
  775.                      /* Uses the Fox/Dawson File Requester; you must link    */
  776.                      /* with glue module myreqglue.o. You need to include    */
  777.                      /* reqbase.h and also libraries/dosextens.h (for the    */
  778.                      /* Process structure).                                  */
  779. {
  780.   struct Screen *ReqScreen;
  781.   struct Window *ReqWindow;
  782.   struct NewScreen ns;
  783.   struct Screen *OpenScreen();
  784.   struct NewWindow nw;
  785.   struct Window *OpenWindow();
  786.  
  787.   struct Task *FindTask();
  788.   struct Process *myprocess;
  789.   APTR oldwindowptr;
  790.  
  791.   /* Link the buffers to the FileRequester struct (all declared as globals) */
  792.  
  793.   MyFileReqStruct.File = filename;
  794.   MyFileReqStruct.Dir = directoryname;
  795.   MyFileReqStruct.PathName = pathname;
  796.  
  797.   /*** Open a window in a custom Hires screen for requester display */
  798.  
  799.   ns.LeftEdge = 0;      /*** Initialize a NewScreen structure */
  800.   ns.TopEdge = 0;
  801.   ns.Width = 640;
  802.   ns.Height = 200;
  803.   ns.Depth = 2;
  804.   ns.DetailPen = 0;  /* Colour of text in screen title bar */
  805.   ns.BlockPen = 1;   /* Colour of screen Title Bar */
  806.   ns.ViewModes = HIRES;
  807.   ns.Type = CUSTOMSCREEN;
  808.   ns.Font = &TOPAZ80;
  809.   ns.DefaultTitle = NULL;
  810.   ns.Gadgets = NULL;
  811.   ns.CustomBitMap = NULL;
  812.  
  813.   nw.LeftEdge = 0;         /*** Initialize a NewWindow structure */
  814.   nw.TopEdge = 0;
  815.   nw.Width = 640;
  816.   nw.Height = 200;
  817.   nw.DetailPen = 0; /* Menu title text color */
  818.   nw.BlockPen  = 1; /* Menu item box and title bar background */
  819.   nw.IDCMPFlags = NULL;
  820.   nw.Flags = ACTIVATE|NOCAREREFRESH;
  821.   nw.FirstGadget = NULL;
  822.   nw.CheckMark = NULL;
  823.   nw.Title = (UBYTE*)" ";
  824.   nw.Screen = NULL;  /* Will set it to ReqScreen after the screen is opened */
  825.   nw.BitMap = NULL;
  826.   nw.MinWidth = 0;
  827.   nw.MinHeight = 0;
  828.   nw.MaxWidth = 0;
  829.   nw.MaxHeight = 0;
  830.   nw.Type = CUSTOMSCREEN;
  831.  
  832.  
  833.   /*** Open a Hires screen to place the requester in */
  834.  
  835.   if ((ReqScreen = (struct Screen *) OpenScreen(&ns)) == NULL) {
  836.     ShowError("Couldn't open Requester screen!!!");
  837.     pathname[0] = '\0'; /* make returned buffer a nullstring */
  838.     return(pathname);
  839.   }
  840.  
  841.   /*** Open the Window to place the requester in */
  842.   nw.Screen = ReqScreen;
  843.   if ((ReqWindow = (struct Window *) OpenWindow(&nw)) == NULL) {
  844.     CloseScreen(ReqScreen);
  845.     ShowError("Couldn't open Requester window!!!");
  846.     pathname[0] = '\0'; /* make returned buffer a nullstring */
  847.     return(pathname);
  848.   }  
  849.  
  850.   /* Set pointer to tell req.library requesters where to appear */
  851.   myprocess = (struct Process *)FindTask((char *)0);
  852.   oldwindowptr = myprocess->pr_WindowPtr;
  853.   myprocess->pr_WindowPtr = (APTR)ReqWindow;
  854.  
  855.   /*** Set up file requester structure fields to customize requester */
  856.  
  857.   MyFileReqStruct.Title = prompt;    /* Text at top of requester */
  858.   MyFileReqStruct.dirnamescolor = 3; /* colours of requester elements */
  859.   MyFileReqStruct.devicenamescolor = 3;
  860.   MyFileReqStruct.WindowLeftEdge = 0;
  861.   MyFileReqStruct.WindowTopEdge = 0;
  862.   MyFileReqStruct.Flags = FRQCACHINGM |    /* Directory Caching */
  863.                        FRQABSOLUTEXYM | /* Fix requester position in screen  */
  864.                        FRQNOHALFCACHEM| /* Don't cache half-read directories */
  865.                        FRQNODRAGM;      /* No drag bar on requester */
  866.  
  867.   if (FileRequester(&MyFileReqStruct) == NULL)   /* Call the File Requester */
  868.     pathname[0] = '\0'; /* If user selected no file, return a nullstring*/
  869.  
  870.   /* restore pr_WindowPtr before closing the window! */
  871.   myprocess->pr_WindowPtr = oldwindowptr;
  872.  
  873.   CloseWindow(ReqWindow);   /*** Close requester screen stuff and return */
  874.   CloseScreen(ReqScreen);
  875.  
  876.   return(pathname); /* Will be the pointer to a string which now contains the */
  877.                     /* full file name selected, or a NULL string if the user */
  878.                     /* selected CANCEL or no filename */
  879. }
  880. /*===========================================================================*/
  881.  
  882. OpenLibraries()     /* Open needed libraries */
  883. {
  884.   IntuitionBase = (struct IntuitionBase *)
  885.           OpenLibrary("intuition.library",INTUITION_REV);
  886.   if (IntuitionBase == NULL) {
  887.     printf("Can't open Intuition library!!!!!!\n");
  888.     CloseAll();
  889.   }
  890.  
  891.   GfxBase = (struct GfxBase *)
  892.           OpenLibrary("graphics.library",GRAPHICS_REV);
  893.   if (GfxBase == NULL) {
  894.     printf("Can't open graphics library!!!!!!\n");
  895.     CloseAll();
  896.   }
  897.  
  898.   MathBase = (void *)OpenLibrary("mathffp.library",0L);
  899.   if (MathBase == NULL) {
  900.     printf("Can't open mathffp library!!!!!!\n");
  901.     CloseAll();
  902.   }
  903.  
  904.   MathTransBase = (void *)OpenLibrary("mathtrans.library",0L);
  905.   if (MathTransBase == NULL) {
  906.     printf("Can't open mathtrans library!!!!!!\n");
  907.     CloseAll();
  908.   }
  909.   ReqBase = (struct ReqLib *)OpenLibrary("req.library", 0L);
  910.   if (ReqBase == NULL) {
  911.     printf("Couldn't open req.library!!!!!!\n");
  912.     printf("IFSlab requires req.library in libs: to be able to do file I/O\n");
  913.   }
  914. }
  915. /*=========================================================================*/
  916.  
  917. OpenDisplay()   /* Open Outline Sketch window in custom screen */
  918.  
  919. {
  920.   /* NEWSCREENSTRUCTURE, PALETTE and NewWindowStructure1 are in IFSLab.h */
  921.   /* and, along with pointers Window, Screen, have been declared externally */
  922.  
  923.   struct Screen *OpenScreen();
  924.   struct Window *OpenWindow();
  925.   struct ViewPort *vp;
  926.  
  927.   /*** Open the screen */
  928.   if ((Screen = (struct Screen *) OpenScreen(&NEWSCREENSTRUCTURE)) == NULL) {
  929.     printf("\nCouldn't open screen!!!!\n");
  930.     CloseAll();
  931.   }
  932.   /*** Link Custom colours defined in PALETTE to screen's ViewPort */  
  933.   vp = &(Screen->ViewPort);/* ptr to viewport structure assoc'd with screen */
  934.   LoadRGB4(vp,PALETTE,16L);   /* Change to custom colors defined above */
  935.  
  936.   /*** Open the Window */
  937.  
  938.   NewWindowStructure1.Screen = Screen;
  939.   if ((Window =  (struct Window *) OpenWindow(&NewWindowStructure1)) == NULL) {
  940.     printf("\nCouldn't open window!!!!\n");
  941.     CloseAll();
  942.   }
  943.  
  944.   scrp = &(Screen->RastPort); /* Screen RastPort,used to draw in outer window*/
  945.   SetDrMd(scrp, JAM1);
  946.   SetAPen(scrp, 1L);
  947.   r = (Window->RPort);  /* Window's rastport, used for drawing in GZZ bitmap */
  948.   SetDrMd(r, JAM1);
  949.   HiliteGadget(0);
  950.   SetMenuStrip(Window,&MenuList1);
  951.   OffMenu(Window, NOITEM<<5 | 2);     /* disable render menu and its items */
  952. }
  953. /*===========================================================================*/
  954.  
  955. OpenNumWindow()   /* Open Coefficients Window and disable menus */
  956.   /* Assumes window is not open already */
  957.  
  958.   NumNewWindowStructure3.Screen = Screen;
  959.   if ((NumWindow = (struct Window *)OpenWindow(&NumNewWindowStructure3))
  960.                                                                  == NULL) {
  961.     ShowError("Error - Couldn't open coeffs window");
  962.     return;
  963.   }
  964.   ClearMenuStrip(Window); /* remove main window menus while Coeffs window on */
  965.   SetWindowTitles(NumWindow, -1L, (char *)"           Collage Editor");
  966.   exists_numwindow = 1;
  967. }
  968. /*===========================================================================*/
  969.  
  970. CloseAll()    /* Close everything neatly and exit program */
  971. {
  972.   if (exists_image)
  973.     CloseImageScreen();
  974.   for (i = 0; i <= N; i++)  /* Dealocate Pieces' struct and piecemap memory */
  975.     FreePiece(pieceptr[i]);
  976.   if (outlinebufptr)
  977.     FreeMem(outlinebufptr, (long)RASSIZE(WIDTH, HEIGHT));
  978.   if (Window) {
  979.     ClearMenuStrip(Window);
  980.     CloseWindow(Window);
  981.   }
  982.   if (exists_numwindow)
  983.     CloseWindow(NumWindow);
  984.   if (Screen)
  985.     CloseScreen(Screen);
  986.   if (IntuitionBase)
  987.     CloseLibrary(IntuitionBase);
  988.   if (GfxBase)
  989.     CloseLibrary(GfxBase);
  990.   if (MathBase)
  991.     CloseLibrary(MathBase);
  992.   if (MathTransBase)
  993.     CloseLibrary(MathTransBase);
  994.   if (ReqBase) {
  995.     PurgeFiles(&MyFileReqStruct);    /* function in req.library */
  996.     CloseLibrary(ReqBase);
  997.   }
  998.  
  999.   exit(0);
  1000. }
  1001. /*===========================================================================*/
  1002.  
  1003. int SetMode(newmode)   /* Sets up and enters requested mode */
  1004. int newmode;           /* newmode can be OUTLINE or COLLAGE */
  1005. {                   /* returns 1 normally; returns 0 if user chose CANCEL in */
  1006.                     /* "IFS will be LOST" requester */
  1007.   BOOL AutoRequest();
  1008.   static  struct IntuiText Cancel = {
  1009.     3,0,                 /* FrontPen, BackPen */
  1010.     JAM1,                /* DrawMode */
  1011.     6,3,                 /* LeftEdge, TopEdge */
  1012.     &TOPAZ80,            /* ITextFont */
  1013.     (UBYTE*)"Cancel",    /* IText */
  1014.     NULL                 /* NextText */
  1015.   };
  1016.   static struct IntuiText Doit, ReqBodyText;
  1017.   Doit = ReqBodyText = Cancel;  /* structure assignment */
  1018.   ReqBodyText.IText = (UBYTE *)" IFS will be LOST!";
  1019.   ReqBodyText.TopEdge = 8;
  1020.   Doit.IText = (UBYTE *)"Do it!";
  1021.  
  1022.   switch (newmode) {
  1023.  
  1024.   case OUTLINE:
  1025.     if (drawmode != VECTOR)     /* already in desired mode! */
  1026.       return(1);
  1027.     if (AutoRequest(Window, &ReqBodyText, &Doit, &Cancel, 
  1028.                           NULL, NULL, 180L, 56L) == FALSE)  {
  1029.       /* Set IDCMP flags right in case AutoRequest() messed 'em up */
  1030.       ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK);
  1031.       return(0);
  1032.     }
  1033.     ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK); /* After AutoReq. */
  1034.     ClearIFS();
  1035.     RemoveGList(Window, &CollageGadgetList2, 6L);
  1036.     AddGList(Window, &GadgetList1, 0L, 6L, NULL); /* change gadgets */
  1037.     SetDrMd(scrp, JAM1);
  1038.     SetAPen(scrp, 0L);
  1039.     RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */
  1040.     RefreshGadgets(&GadgetList1, Window, NULL);
  1041.     OffMenu(Window, NOITEM<<5 | 2);     /* disable render menu and its items */
  1042.     SetWindowTitles(Window, -1L, "           Outline Editor");
  1043.     drawmode = FREEHAND;
  1044.     HiliteGadget(0);
  1045.     SetAPen(r, 2L);
  1046.     break;
  1047.  
  1048.   case COLLAGE:
  1049.     if (drawmode == VECTOR)     /* already in desired mode! */
  1050.       return(1);
  1051.     if (exists_outline == 0)
  1052.       PutDefOutline();
  1053.  
  1054.     CopyMem((char*)Window->RPort->BitMap->Planes[1], 
  1055.       (char*)outlinebufptr, (long)RASSIZE(WIDTH,HEIGHT));
  1056.  
  1057.     RemoveGList(Window, &GadgetList1, 6L);
  1058.     AddGList(Window, &CollageGadgetList2, 0L, 6L, NULL);
  1059.     SetDrMd(scrp, JAM1);
  1060.     SetAPen(scrp, 0L);
  1061.     RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */
  1062.     RefreshGadgets(&CollageGadgetList2, Window, NULL);
  1063.     OnMenu(Window, NOITEM<<5 | 2);     /* Enable render menu and its items */
  1064.     SetWindowTitles(Window, -1L, "           Collage Editor");
  1065.     drawmode = VECTOR;
  1066.     curcor = '0';
  1067.     break;
  1068.   }    /* end switch newmode */
  1069.   return(1);
  1070. }
  1071. /*===========================================================================*/
  1072.  
  1073. DupSelPiece()       /* Generate a copy of Selected Piece, make it highest */
  1074. {                   /* numbered and Selected. Increment N. */
  1075.   if (N >= MAX_N) {
  1076.     ShowError("Too many transformations");
  1077.     return;
  1078.   }
  1079.   /*** Allocate and fill piece structure, make it Selected */
  1080.  
  1081.   if ((pieceptr[++N] = AllocPiece()) == NULL) {  /* Allocate Piece */
  1082.     N--;         /* if couldn't allocate piece */
  1083.     if (N == -1) {    /* No memory for even one Piece - Quit program */
  1084.       ShowError("Re-run with more free memory!");
  1085.       CloseAll();
  1086.     }
  1087.     return;
  1088.   }
  1089.   pieceptr[N]->a = pieceptr[selpiece]->a;
  1090.   pieceptr[N]->b = pieceptr[selpiece]->b;
  1091.   pieceptr[N]->c = pieceptr[selpiece]->c;
  1092.   pieceptr[N]->d = pieceptr[selpiece]->d;
  1093.   pieceptr[N]->e = pieceptr[selpiece]->e;
  1094.   pieceptr[N]->f = pieceptr[selpiece]->f;
  1095.   pieceptr[N]->s1 = pieceptr[selpiece]->s1;
  1096.   pieceptr[N]->s2 = pieceptr[selpiece]->s2;
  1097.   pieceptr[N]->r1 = pieceptr[selpiece]->r1;
  1098.   pieceptr[N]->r2 = pieceptr[selpiece]->r2;
  1099.   pieceptr[N]->dens = 1.0;
  1100.   pieceptr[N]->det = pieceptr[selpiece]->det;
  1101.   pieceptr[N]->boxo = pieceptr[selpiece]->boxo;  /* Structure assignment! */
  1102.   pieceptr[N]->boxx = pieceptr[selpiece]->boxx;  /* Structure assignment! */
  1103.   pieceptr[N]->boxy = pieceptr[selpiece]->boxy;  /* Structure assignment! */
  1104.   pieceptr[N]->boxz = pieceptr[selpiece]->boxz;  /* Structure assignment! */
  1105.  
  1106.   ComputeProbs();  /* Adjust all Piece probabilities */
  1107.   /* Copy selected piece's piecemap to the new piece's piecemap */
  1108.   CopyMem((char*)pieceptr[selpiece]->piecemap, (char*)pieceptr[N]->piecemap,
  1109.                                               (long)RASSIZE(WIDTH,GZZHEIGHT));
  1110.   selpiece = N;   /* make this Piece Selected */
  1111. }
  1112. /*===========================================================================*/
  1113.  
  1114. AddDefPiece()       /* Generate a default Piece of current Outline, make */
  1115. {                   /* it highest numbered and Selected. Increment N. */
  1116.   if (N >= MAX_N) {
  1117.     ShowError("Too many transformations");
  1118.     return;
  1119.   }
  1120.   /*** Allocate and fill piece structure, make it Selected */
  1121.  
  1122.   if ((pieceptr[++N] = AllocPiece()) == NULL) {  /* Allocate Piece */
  1123.     N--;         /* if couldn't allocate piece */
  1124.     if (N == -1) {    /* No memory for even one Piece - Quit program */
  1125.       ShowError("Re-run with more free memory!");
  1126.       CloseAll();
  1127.     }
  1128.     return;
  1129.   }
  1130.   pieceptr[N]->a = 0.5;
  1131.   pieceptr[N]->b = 0.0;
  1132.   pieceptr[N]->c = 0.0;
  1133.   pieceptr[N]->d = 0.5;
  1134.   pieceptr[N]->e = 0.25;              
  1135.   pieceptr[N]->f = 0.16938406;    /*   = 0.25*GZZHEIGHT/GZZWIDTH */
  1136.   pieceptr[N]->s1 = 0.5;
  1137.   pieceptr[N]->s2 = 0.5;
  1138.   pieceptr[N]->r1 = 0.0;
  1139.   pieceptr[N]->r2 = 0.0;
  1140.   pieceptr[N]->dens = 1.0;
  1141.   pieceptr[N]->det = 0.25;
  1142.   pieceptr[N]->boxo.x = 0.25 * GZZWIDTH;
  1143.   pieceptr[N]->boxo.y = 0.25 * GZZHEIGHT;
  1144.   pieceptr[N]->boxx.x = 0.75 * GZZWIDTH;
  1145.   pieceptr[N]->boxx.y = 0.25 * GZZHEIGHT;
  1146.   pieceptr[N]->boxy.x = 0.25 * GZZWIDTH;
  1147.   pieceptr[N]->boxy.y = 0.75 * GZZHEIGHT;
  1148.   pieceptr[N]->boxz.x = 0.75 * GZZWIDTH;
  1149.   pieceptr[N]->boxz.y = 0.75 * GZZHEIGHT;
  1150.  
  1151.   ComputeProbs();  /* Adjust all Piece probabilities */
  1152.   /* Transform Outline by this piece's transformation into its piecemap */
  1153.   TransformPiece(outlinebufptr, pieceptr[N]->piecemap, pieceptr[N]);
  1154.   selpiece = N;   /* make this Piece Selected */
  1155. }
  1156. /*===========================================================================*/
  1157.  
  1158. ComputeProbs()       /* Recompute all piece probabilities */
  1159. {
  1160.   double sum = 0;
  1161.  
  1162.   for (i = 0; i <= N; i++)
  1163.     sum = sum + pieceptr[i]->dens * pieceptr[i]->det;
  1164.  
  1165.   for (i = 0; i <= N; i++)
  1166.     pieceptr[i]->p = (pieceptr[i]->dens * pieceptr[i]->det)/sum;
  1167. }
  1168. /*===========================================================================*/
  1169.  
  1170. DrawBox()   /* Erase bitplane 3, and draw Vector Box of Selected Piece in it */
  1171. {
  1172.   SetWrMsk(r,0xFFF8);  /* Write-protect planes 0,1,2 */
  1173.   SetRast(r, 0L);      /* Erase plane 3 */
  1174.  
  1175.   SetAPen(r, 8L);    /*** Draw Vector Box */
  1176.   Move(r, pieceptr[selpiece]->boxo.x, pieceptr[selpiece]->boxo.y);
  1177.   Draw(r, pieceptr[selpiece]->boxx.x, pieceptr[selpiece]->boxx.y);
  1178.   Draw(r, pieceptr[selpiece]->boxz.x, pieceptr[selpiece]->boxz.y);
  1179.   Draw(r, pieceptr[selpiece]->boxy.x, pieceptr[selpiece]->boxy.y);
  1180.   Draw(r, pieceptr[selpiece]->boxo.x, pieceptr[selpiece]->boxo.y);
  1181.  
  1182.   SetDrMd(r, JAM2|INVERSVID); /*** Draw Corner "gadgets" */
  1183.   SetBPen(r, 0L);
  1184.   Move(r, pieceptr[selpiece]->boxo.x - 3L, pieceptr[selpiece]->boxo.y + 3L);
  1185.   Text(r, "O", 1L);
  1186.   Move(r, pieceptr[selpiece]->boxx.x - 3L, pieceptr[selpiece]->boxx.y + 3L);
  1187.   Text(r, "x", 1L);
  1188.   Move(r, pieceptr[selpiece]->boxz.x - 3L, pieceptr[selpiece]->boxz.y + 3L);
  1189.   Text(r, " ", 1L);
  1190.   Move(r, pieceptr[selpiece]->boxy.x - 3L, pieceptr[selpiece]->boxy.y + 3L);
  1191.   Text(r, "y", 1L);
  1192.  
  1193.   SetDrMd(r, JAM1);
  1194.   SetWrMsk(r,0xFFFF);
  1195. }
  1196. /*===========================================================================*/
  1197.  
  1198. ToggleGhostBox()        /* Draw or undraw current ghost box, as given in */
  1199. {                                /* ghbox? variables, in COMPLEMENT mode */
  1200.   SetDrMd(r, JAM1|COMPLEMENT);
  1201.   Move(r, ghboxo.x, ghboxo.y);
  1202.   Draw(r, ghboxx.x, ghboxx.y);
  1203.   Draw(r, ghboxz.x, ghboxz.y);
  1204.   Draw(r, ghboxy.x, ghboxy.y);
  1205.   Draw(r, ghboxo.x, ghboxo.y);
  1206.   SetDrMd(r, JAM1);
  1207.  
  1208. }
  1209. /*===========================================================================*/
  1210.  
  1211. ComputeNewBox()    /* Compute New Ghost Box from mouse coords, put in tmpbox */
  1212. {                  /* variables. */
  1213.   long dx, dy;   /* increments of mouse position from corner's previous pos */
  1214.   double s, a;   /* Scaling and Rotation components of change in vector Z */
  1215.   double cosa, sina;   /* cosine and sine of angle a */
  1216.   double Qx, Qy;
  1217.   double tmp;
  1218.  
  1219.   switch (curcor) { 
  1220.  
  1221.   case 'o':
  1222.     dx = mx - pieceptr[selpiece]->boxo.x;
  1223.     dy = my - pieceptr[selpiece]->boxo.y;
  1224.     tmpboxo.x = mx;
  1225.     tmpboxo.y = my;
  1226.     tmpboxx.x = pieceptr[selpiece]->boxx.x + dx;
  1227.     tmpboxx.y = pieceptr[selpiece]->boxx.y + dy;
  1228.     tmpboxy.x = pieceptr[selpiece]->boxy.x + dx;
  1229.     tmpboxy.y = pieceptr[selpiece]->boxy.y + dy;
  1230.     tmpboxz.x = pieceptr[selpiece]->boxz.x + dx;
  1231.     tmpboxz.y = pieceptr[selpiece]->boxz.y + dy;
  1232.     break;
  1233.  
  1234.   case 'x':
  1235.     dx = mx - pieceptr[selpiece]->boxx.x;
  1236.     dy = my - pieceptr[selpiece]->boxx.y;
  1237.     tmpboxo = pieceptr[selpiece]->boxo;   /* Structure Assignment */
  1238.     tmpboxx.x = mx;
  1239.     tmpboxx.y = my;
  1240.     tmpboxy = pieceptr[selpiece]->boxy;   /* Structure Assignment */
  1241.     tmpboxz.x = pieceptr[selpiece]->boxz.x + dx;
  1242.     tmpboxz.y = pieceptr[selpiece]->boxz.y + dy;
  1243.     break;
  1244.  
  1245.   case 'y':
  1246.     dx = mx - pieceptr[selpiece]->boxy.x;
  1247.     dy = my - pieceptr[selpiece]->boxy.y;
  1248.     tmpboxo = pieceptr[selpiece]->boxo;   /* Structure Assignment */
  1249.     tmpboxx = pieceptr[selpiece]->boxx;   /* Structure Assignment */
  1250.     tmpboxy.x = mx;
  1251.     tmpboxy.y = my;
  1252.     tmpboxz.x = pieceptr[selpiece]->boxz.x + dx;
  1253.     tmpboxz.y = pieceptr[selpiece]->boxz.y + dy;
  1254.     break;
  1255.  
  1256.   case 'z':
  1257.     s = sqrt((double)((mx-pieceptr[selpiece]->boxo.x)*
  1258.      (mx-pieceptr[selpiece]->boxo.x)+(my-pieceptr[selpiece]->boxo.y)*
  1259.      (my-pieceptr[selpiece]->boxo.y))/
  1260.      (double)((pieceptr[selpiece]->boxz.x-pieceptr[selpiece]->boxo.x)*
  1261.      (pieceptr[selpiece]->boxz.x-pieceptr[selpiece]->boxo.x)+
  1262.      (pieceptr[selpiece]->boxz.y-pieceptr[selpiece]->boxo.y)*
  1263.      (pieceptr[selpiece]->boxz.y-pieceptr[selpiece]->boxo.y)));
  1264.  
  1265.     a = myatan2((double)(my - pieceptr[selpiece]->boxo.y), (double)(mx - 
  1266.      pieceptr[selpiece]->boxo.x)) - myatan2((double)
  1267.      (pieceptr[selpiece]->boxz.y - pieceptr[selpiece]->boxo.y), 
  1268.      (double)(pieceptr[selpiece]->boxz.x - pieceptr[selpiece]->boxo.x));
  1269.  
  1270.     cosa = cos(a);
  1271.     sina = sin(a);
  1272.  
  1273.     Qx = pieceptr[selpiece]->boxo.x + s * (pieceptr[selpiece]->boxo.y * sina -
  1274.      pieceptr[selpiece]->boxo.x * cosa);
  1275.     Qy = pieceptr[selpiece]->boxo.y - s * (pieceptr[selpiece]->boxo.y * cosa +
  1276.      pieceptr[selpiece]->boxo.x * sina);
  1277.  
  1278.     tmpboxo = pieceptr[selpiece]->boxo;   /* Structure Assignment */
  1279.  
  1280.     /* Below, the conditional assignments assure rounding on the */
  1281.     /* double-to-int conversion rather than truncation */
  1282.     tmp = s * (pieceptr[selpiece]->boxx.x * cosa - 
  1283.      pieceptr[selpiece]->boxx.y * sina) + Qx;
  1284.     tmpboxx.x = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp;
  1285.     tmp = s * (pieceptr[selpiece]->boxx.x * sina + 
  1286.      pieceptr[selpiece]->boxx.y * cosa) + Qy;
  1287.     tmpboxx.y = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp;
  1288.     tmp = s * (pieceptr[selpiece]->boxy.x * cosa - 
  1289.      pieceptr[selpiece]->boxy.y * sina) + Qx;
  1290.     tmpboxy.x = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp;
  1291.     tmp = s * (pieceptr[selpiece]->boxy.x * sina + 
  1292.      pieceptr[selpiece]->boxy.y * cosa) + Qy;
  1293.     tmpboxy.y = (tmp - floor(tmp) > 0.5) ? tmp + 1. : tmp;
  1294.     tmpboxz.x = tmpboxy.x + tmpboxx.x - tmpboxo.x;
  1295.     tmpboxz.y = tmpboxx.y + tmpboxy.y - tmpboxo.y;
  1296.     break;
  1297.   }         /* End switch curcor */
  1298. }
  1299. /*===========================================================================*/
  1300.  
  1301. DeleteSelPiece()  /* Delete the Selected Piece. If it was the only piece,  */
  1302. {                 /* replace it with a Default Piece. Rearrange Piece list */
  1303.   FreePiece(pieceptr[selpiece]);   /* free memory associated with the piece */
  1304.   for (i = selpiece; i < N; i++)   /* close gap in pointer array */
  1305.     pieceptr[i] = pieceptr[i+1];
  1306.   if (selpiece == N)
  1307.     selpiece--;
  1308.   N--;
  1309.  
  1310.   if (N == -1)     /* if deleted last piece, place a default piece */
  1311.     AddDefPiece();
  1312. }
  1313. /*==========================================================================*/
  1314.  
  1315. ClearIFS()  /* Clear IFS variables, memory, and Collage image */ 
  1316. {
  1317.   /* Clear all GZZ bitplanes except Outline plane */
  1318.   SetWrMsk(r,0xFFFD);  /* Write-protect plane 1, Outline */
  1319.   SetRast(r, 0L);
  1320.   SetWrMsk(r,0xFFFF);
  1321.  
  1322.   for(i = 0; i <= N; i++)
  1323.     FreePiece(pieceptr[i]);
  1324.   N = -1;
  1325. }
  1326. /*==========================================================================*/
  1327.  
  1328. HiliteGadget(gadgetid)        /* frame gadget of given ID# in Red */
  1329.         /* Implements in software Gadget Mutual Exclude */
  1330.         /* Because window is GZZ and gadgets are in its border, this routine */
  1331.         /* Draws the frame directly on the Screen,not in Window. It is quite */
  1332.         /* non-generic because gadget coordinates are coded in numerically */
  1333. int gadgetid;
  1334. {
  1335.   long i;
  1336.      /*** Define frames as border structures ***/
  1337.  
  1338.   static SHORT Frame_data[10] = { 0,0, 36,0, 36,15, 0,15, 0,0 };
  1339.                                               /* Gadget select frame data */
  1340.  
  1341.   static struct Border OnFrame_Bdr = {  /* Red (Selected) Gadget border */
  1342.      0,0,                 /* LeftEdge, TopEdge */
  1343.      4,0,JAM1,            /* FrontPen, BackPen, DrawMode */
  1344.      5,                   /* Count */
  1345.      Frame_data,          /* XY */
  1346.      NULL                 /* Next Border */
  1347.   };
  1348.  
  1349.   static struct Border OffFrame_Bdr = { /* Lt Green (deselected) Gadg border */
  1350.      0,0,                 /* LeftEdge, TopEdge */
  1351.      3,0,JAM1,            /* FrontPen, BackPen, DrawMode */
  1352.      5,                   /* Count */
  1353.      Frame_data,          /* XY */
  1354.      NULL                 /* Next Border */
  1355.   };
  1356.  
  1357.   /*** Unframe all gadgets, then frame selected one */ 
  1358.   for (i = 0; i <= 3; i++)
  1359.     DrawBorder(scrp, &OffFrame_Bdr, 280L, i*18L+12L);
  1360.   DrawBorder(scrp, &OnFrame_Bdr, 280L, gadgetid*18L+12L);
  1361. }
  1362. /*==========================================================================*/
  1363.  
  1364. DrawCollage()    /* Draw the current IFS as a collage image with */
  1365. {                /* vector Box of Selected Piece (wipe out old box)*/
  1366.   /*** Erase all bitplanes except Outline plane */
  1367.   SetWrMsk(r,0xFFFD);  /* Write-protect plane 1, Outline */
  1368.   SetRast(r, 0L);
  1369.  
  1370.   /* Blit Deselected pieces from piecemaps to plane 0 */
  1371.   SetWrMsk(r,0x0001);  /* Write protect all planes except plane 0 */
  1372.   SetAPen(r, 1L);
  1373.   for (i = 0; i <= N; i++)
  1374.     if (i != selpiece)    /* skip Selected piece */
  1375.       BltTemplate((char*)pieceptr[i]->piecemap,(long)Window->BorderLeft,
  1376.         (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT);
  1377.   /* Blit Selected Piece from piecemap to plane 2 */
  1378.   SetWrMsk(r,0x0004);  /* Write protect all planes except plane 2 */
  1379.   SetAPen(r, 4L);
  1380.   BltTemplate((char*)pieceptr[selpiece]->piecemap,(long)Window->BorderLeft,
  1381.    (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT);
  1382.   
  1383.   DrawBox();  /* Draw Vector Box of Selected Piece */
  1384.   /* Note that Rastport's WrMask is restored within DrawBox() */
  1385. }
  1386. /*==========================================================================*/
  1387.  
  1388. double myatan2(y,x) /* atan2() as defined in Aztec C but missing from mf.lib */
  1389. double x, y;        /* returns arctangent of y/x in range -pi to pi.         */
  1390. {                   /* If x=y=0, returns 0, sets errno=EDOM. */
  1391.                     /* Requires #include of errno.h and math.h */
  1392.  
  1393.   #define PI 3.141592654
  1394.   if (x == 0.0) {
  1395.     if (y == 0.0) {
  1396.       errno = EDOM;
  1397.       return(0.0);
  1398.     }
  1399.     else
  1400.       x = 1.0e-10;
  1401.   }
  1402.  
  1403.   if (y >= 0.0) {
  1404.     if (x >= 0.0)
  1405.       return(atan(y/x));        /* Quadrant I */
  1406.     else
  1407.       return(PI - atan(-y/x));  /* Quadrant II */
  1408.   }
  1409.   else {
  1410.     if (x >= 0.0)
  1411.       return(-atan(-y/x));      /* Quadrant IV */
  1412.     else
  1413.       return(atan(y/x) - PI);      /* Quadrant III */
  1414.   }
  1415. }
  1416. /*==========================================================================*/
  1417.  
  1418. ModifyMousePtr(Wind, n)
  1419.   /* Change to custom mouse pointer in Window if n=1, or restore default */
  1420.   /* arrow and free CHIP RAM if n=-1. Use in only one window at a time! */
  1421. struct Window *Wind;
  1422. int n;           
  1423. {
  1424.   static short *ChipPtr;
  1425.   static struct ViewPort *vprt;
  1426.   static long color17, color18, color19;
  1427.   long GetRGB4();
  1428.  
  1429.   /*** Define  Custom mouse pointer sprite data */
  1430.  
  1431.   #define ROWS 14    /* Number of Pixel rows in pointer */
  1432.  
  1433.   static USHORT Mouseptr[] = {
  1434.     0x0000, 0x0000,
  1435.                                   /*  ++++++++++++++++++  */
  1436.     0x00f0, 0x00f0,               /*  +        3333    +  */
  1437.     0x03f8, 0x03f8,               /*  +      3333333   +  */
  1438.     0x07fc, 0x079c,               /*  +     333311333  +  */
  1439.     0x0ffe, 0x0e0e,               /*  +    33311111333 +  */
  1440.     0x0ffe, 0x0c06,               /*  +    33111111133 +  */
  1441.     0xafff, 0x0c73,               /*  +1 1 331113331133+  */
  1442.     0xafff, 0x0cfb,               /*  +1 1 331133333133+  */
  1443.     0xafff, 0x0ccb,               /*  +1 1 331133113133+  */
  1444.     0xcfff, 0x0cdb,               /*  +11  331133133133+  */
  1445.     0xdfff, 0x18c3,               /*  +11 3311133111133+  */
  1446.     0xffff, 0x1ce7,               /*  +1113331133311333+  */
  1447.     0xfffe, 0x07fe,               /*  +111113333333333 +  */
  1448.     0x7ffe, 0x003c,               /*  + 11111111133331 +  */
  1449.     0x1fff, 0x0000,               /*  +   1111111111111+  */
  1450.                                   /*  ++++++++++++++++++  */
  1451.     0x0000, 0x0000
  1452.   };
  1453.  
  1454.   if (n == 1 && ChipPtr == NULL) { 
  1455.  
  1456.     /*** Get colors of default pointer in Window's screen */
  1457.     vprt = &(Wind->WScreen->ViewPort); /* ptr to viewport of screen */
  1458.     color17 = GetRGB4(vprt->ColorMap, 17L);
  1459.     color18 = GetRGB4(vprt->ColorMap, 18L);
  1460.     color19 = GetRGB4(vprt->ColorMap, 19L);
  1461.  
  1462.     /*** Copy sprite data into chip RAM to be accessible to graphic chips */
  1463.     ChipPtr =(short*)AllocMem((long)((ROWS+2)*4),MEMF_CHIP); /* Alloc block */
  1464.     CopyMem((char*)Mouseptr,(char*)ChipPtr,(long)((ROWS+2)*4)); /* Copy data*/
  1465.     /*** Attach custom pointer to window */
  1466.     SetPointer(Wind, ChipPtr, (long)ROWS, 16L, -1L, -1L);
  1467.  
  1468.     /*** Change custom pointer colors */
  1469.     SetRGB4(vprt, 17L, 15L, 12L, 10L);
  1470.     SetRGB4(vprt, 18L, 13L, 2L, 2L); 
  1471.     SetRGB4(vprt, 19L, 15L, 0L, 0L); 
  1472.   }
  1473.   else if (n == -1 && ChipPtr != NULL) {
  1474.     ClearPointer(Wind);     /* Reset default pointer and its original colors */
  1475.     SetRGB4(vprt, 17L,  color17>>8, (color17>>4L)&15, color17&15);
  1476.     SetRGB4(vprt, 18L,  color18>>8, (color18>>4L)&15, color18&15);
  1477.     SetRGB4(vprt, 19L,  color19>>8, (color19>>4L)&15, color19&15);
  1478.     FreeMem(ChipPtr, (long)((ROWS+2)*4)); /* release memory of pointer data */
  1479.     ChipPtr = NULL;
  1480.   }
  1481.   else 
  1482.    DisplayBeep(NULL);  /* Illegal call (called twice in a row with same n */
  1483. }
  1484. /*===========================================================================*/
  1485.  
  1486. UpdateNumStrings()   /* Update values of Coeff window string gadgets from */
  1487. {       /* selected piece structure. Redraw gadgets if the window is open */
  1488.   double degr1, degr2;
  1489.  
  1490.   degr1 = pieceptr[selpiece]->r1 / 0.017453293; /* convert radians to degrees*/
  1491.   degr2 = pieceptr[selpiece]->r2 / 0.017453293;
  1492.  
  1493.   sprintf((char*)NumNumT2coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->f);
  1494.   sprintf((char*)NumNumT1coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->e);
  1495.   sprintf((char*)NumNumR2coeffGadgSIBuff, "%5.lf", degr2);
  1496.   sprintf((char*)NumNumR1coeffGadgSIBuff, "%5.lf", degr1);
  1497.   sprintf((char*)NumNumS2coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->s2);
  1498.   sprintf((char*)NumNumS1coeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->s1);
  1499.   sprintf((char*)NumNumPcoeffGadgSIBuff, "%5.3lf", pieceptr[selpiece]->p);
  1500.   sprintf((char*)NumNumDenscoeffGadgSIBuff,"%4.1lf", pieceptr[selpiece]->dens);
  1501.   sprintf((char*)NumNumFcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->f);
  1502.   sprintf((char*)NumNumEcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->e);
  1503.   sprintf((char*)NumNumDcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->d);
  1504.   sprintf((char*)NumNumCcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->c);
  1505.   sprintf((char*)NumNumBcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->b);
  1506.   sprintf((char*)NumNumAcoeffGadgSIBuff, "%5.2lf", pieceptr[selpiece]->a);
  1507.   if (exists_numwindow)
  1508.     RefreshGadgets(&NumGadgetList3, NumWindow, NULL);
  1509. }
  1510. /*===========================================================================*/
  1511.  
  1512. RecomputeCoeffs(gadgetid)         /* Re-Calculate 13 coefficients based on */
  1513. int gadgetid;      /* change in value of the one in the string gadget with */
  1514. {                  /* the given gadgetid; put new values in string gadgets */
  1515.                    /* Does range checking on entered coefficients; If user */
  1516.                    /* entered an illegal value, will restore previous      */
  1517.                    /* value and inform user before returning.              */
  1518.   double a, b, c, d, e, f, s1, s2, r1, r2, t1, t2, dens, p;
  1519.   double sum, p0, det, olddet, olddens;
  1520.  
  1521.   /* Put current coefficient values into temporary variables */
  1522.   sscanf((char*)NumNumT2coeffGadgSIBuff, "%lf", &t2);
  1523.   sscanf((char*)NumNumT1coeffGadgSIBuff, "%lf", &t1);
  1524.   sscanf((char*)NumNumR2coeffGadgSIBuff, "%lf", &r2);
  1525.   sscanf((char*)NumNumR1coeffGadgSIBuff, "%lf", &r1);
  1526.   sscanf((char*)NumNumS2coeffGadgSIBuff, "%lf", &s2);
  1527.   sscanf((char*)NumNumS1coeffGadgSIBuff, "%lf", &s1);
  1528.   sscanf((char*)NumNumPcoeffGadgSIBuff, "%lf", &p);
  1529.   sscanf((char*)NumNumDenscoeffGadgSIBuff, "%lf", &dens);
  1530.   sscanf((char*)NumNumFcoeffGadgSIBuff, "%lf", &f);
  1531.   sscanf((char*)NumNumEcoeffGadgSIBuff, "%lf", &e);
  1532.   sscanf((char*)NumNumDcoeffGadgSIBuff, "%lf", &d);
  1533.   sscanf((char*)NumNumCcoeffGadgSIBuff, "%lf", &c);
  1534.   sscanf((char*)NumNumBcoeffGadgSIBuff, "%lf", &b);
  1535.   sscanf((char*)NumNumAcoeffGadgSIBuff, "%lf", &a);
  1536.  
  1537.   while (r1 > 180. || r1 < -180.) /* Get angles to range -180 => +180 */
  1538.     r1 = (r1 > 0.) ? r1 - 360. : r1 + 360.;
  1539.   while (r2 > 180. || r2 < -180.)
  1540.     r2 = (r2 > 0.) ? r2 - 360. : r2 + 360.;
  1541.  
  1542.   r1 = r1*0.017453293;            /* convert degrees to radians*/
  1543.   r2 = r2*0.017453293;
  1544.  
  1545.   /*** Check that all coefficients are in legal ranges */
  1546.   if (a < -1. || a > 1. || b < -1. || b > 1. || c < -1. || c > 1. ||
  1547.       d < -1. || d > 1. || e < -0.25 || e > 1. || f < -0.25 || f > 1. ||
  1548.       s1 < -1. || s1 > 1. || s2 < -1. || s2 > 1. || dens <= 0. ||
  1549.       t1 < -0.25 || t1 > 1. || t2 < -0.25 || t2 > 1. || p <= 0. || p > 1.) {
  1550.     ShowError("Coefficient out of Range!");
  1551.     a = pieceptr[selpiece]->a;    /*** Undo the error */
  1552.     b = pieceptr[selpiece]->b;
  1553.     c = pieceptr[selpiece]->c;
  1554.     d = pieceptr[selpiece]->d;
  1555.     e = pieceptr[selpiece]->e;
  1556.     f = pieceptr[selpiece]->f;
  1557.     p = pieceptr[selpiece]->p;
  1558.     dens = pieceptr[selpiece]->dens;
  1559.     s1 = pieceptr[selpiece]->s1;
  1560.     s2 = pieceptr[selpiece]->s2;
  1561.     r1 = pieceptr[selpiece]->r1;
  1562.     r2 = pieceptr[selpiece]->r2;
  1563.     t1 = pieceptr[selpiece]->e;
  1564.     t2 = pieceptr[selpiece]->f;
  1565.   }
  1566.  
  1567.   det = fabs(a * d - b * c);
  1568.   if (det == 0.0)
  1569.     det = 0.01;
  1570.   sum = -(pieceptr[selpiece]->dens * pieceptr[selpiece]->det) + dens * det;
  1571.   for (i = 0; i <= N; i++)
  1572.     sum = sum + pieceptr[i]->dens * pieceptr[i]->det;
  1573.  
  1574.   if (gadgetid <= 26) {   /* id's 21 thru 26, matrix coeffs a - f */
  1575.     s1 = sqrt(a*a + c*c);
  1576.     s2 = sqrt(b*b + d*d);
  1577.     r1 = myatan2(c, a);
  1578.     r2 = myatan2(-b, d);
  1579.     t1 = e;
  1580.     t2 = f;
  1581.     p = (dens * det)/sum;
  1582.   }
  1583.  
  1584.   if (gadgetid == 27) {   /* density coefficient */
  1585.     p = (dens * det)/sum;
  1586.   }
  1587.  
  1588.   if (gadgetid == 28)  {   /* probability coefficient */
  1589.     olddens = dens;
  1590.     p0 = det / (sum - olddens*det + det); /* What p SHOULD be with dens == 1 */
  1591.     dens = p / p0;
  1592.     p = (dens * det)/(sum - olddens*det + dens*det);
  1593.   }
  1594.  
  1595.   if (gadgetid >= 29) {   /* id's 29 thru 34, geometrical coeffs s1 thru t2 */
  1596.     olddet = fabs(a * d - b * c);
  1597.     if (olddet == 0.0)
  1598.       olddet = 0.01;
  1599.     a = s1 * cos(r1);
  1600.     b = -s2 * sin(r2);
  1601.     c = s1 * sin(r1);
  1602.     d = s2 * cos(r2);
  1603.     e = t1;
  1604.     f = t2;
  1605.     det = fabs(a * d - b * c);  /*** recompute det and probability */
  1606.     if (det == 0.0)
  1607.       det = 0.01;
  1608.     sum = sum - olddet*dens + det*dens;
  1609.     p = (dens * det)/sum;
  1610.   }
  1611.  
  1612.   r1 = r1 / 0.017453293; /* convert radians to degrees */
  1613.   r2 = r2 / 0.017453293;
  1614.  
  1615.   /* Put resultant coefficient values into their string gadgets */
  1616.   sprintf((char*)NumNumT2coeffGadgSIBuff, "%5.2lf", f);
  1617.   sprintf((char*)NumNumT1coeffGadgSIBuff, "%5.2lf", e);
  1618.   sprintf((char*)NumNumR2coeffGadgSIBuff, "%5.lf", r2);
  1619.   sprintf((char*)NumNumR1coeffGadgSIBuff, "%5.lf", r1);
  1620.   sprintf((char*)NumNumS2coeffGadgSIBuff, "%5.2lf", s2);
  1621.   sprintf((char*)NumNumS1coeffGadgSIBuff, "%5.2lf", s1);
  1622.   sprintf((char*)NumNumPcoeffGadgSIBuff, "%5.3lf", p);
  1623.   sprintf((char*)NumNumDenscoeffGadgSIBuff, "%4.1lf", dens);
  1624.   sprintf((char*)NumNumFcoeffGadgSIBuff, "%5.2lf", f);
  1625.   sprintf((char*)NumNumEcoeffGadgSIBuff, "%5.2lf", e);
  1626.   sprintf((char*)NumNumDcoeffGadgSIBuff, "%5.2lf", d);
  1627.   sprintf((char*)NumNumCcoeffGadgSIBuff, "%5.2lf", c);
  1628.   sprintf((char*)NumNumBcoeffGadgSIBuff, "%5.2lf", b);
  1629.   sprintf((char*)NumNumAcoeffGadgSIBuff, "%5.2lf", a);
  1630.  
  1631.   return;
  1632. }
  1633. /*==========================================================================*/
  1634.  
  1635. ShowError(text)    /* Show error message in a Window, return on user */
  1636.                    /* clicking in the OK gadget. Text string must not */
  1637.                    /* exceed Window's width minus two characters. */
  1638.                    /* Window is used and not a requester so it is in front */
  1639.                    /* of coeffs window & GZZ Border of main Window */
  1640. char *text;  /* The message to be displayed */
  1641. {
  1642.   struct Message * GetMsg();
  1643.   void ReplyMsg();
  1644.   struct IntuiMessage *message;
  1645.   int gadgetid;
  1646.   struct Window *ErrWindow, *OpenWindow();
  1647.  
  1648.   #define TEXTCOLOR 3      /* Text color for message and "OK" */
  1649.   #define BGCOLOR   14     /* Color for Window and Gadget background */
  1650.   #define GADGBDRCOLOR 3   /* Color for border of "OK" Gadget */
  1651.   #define BDRCOLOR 3       /* Color for border of Error-window */
  1652.  
  1653.   /*** Initialize Error-window structures */
  1654.  
  1655.   static  struct IntuiText OKGadgetMsg = {
  1656.     TEXTCOLOR, BGCOLOR,  /* FrontPen, BackPen */
  1657.     JAM1,                /* DrawMode */
  1658.     4,3,                 /* LeftEdge, TopEdge */
  1659.     NULL,                /* ITextFont */
  1660.     (UBYTE*)"OK",        /* IText */
  1661.     NULL                 /* NextText */
  1662.   };
  1663.  
  1664.   static SHORT OKGadgetCoord[18] = { 0,0, 23,0, 23,12, 0,12, 0,0,
  1665.                                      24,0, 24,12, -1,12, -1,0 };
  1666.   /* NOTE: Second line above gives 2-pixel-wide border for hires screen */
  1667.  
  1668.   static struct Border OKGadgetBdr = {
  1669.     0,0,                 /* LeftEdge, TopEdge */
  1670.     GADGBDRCOLOR, BGCOLOR, JAM1,   /* FrontPen, BackPen, DrawMode */
  1671.     9,                   /* Count */
  1672.     &OKGadgetCoord[0],   /* XY */
  1673.     NULL                 /* NextBorder */
  1674.   };
  1675.  
  1676.   static struct Gadget OKGadget = {
  1677.     NULL,                /*  NextGadget */
  1678.     -40,35,24,13,       /* LeftEdge, TopEdge, Width, Height */
  1679.     GADGHCOMP | GRELRIGHT,  /* Flags */
  1680.     ENDGADGET |
  1681.     RELVERIFY,           /* Activation */
  1682.     BOOLGADGET,          /* GadgetType */
  1683.     (APTR)&OKGadgetBdr,  /* GadgetRender */
  1684.     NULL,                /* SelectRender */
  1685.     &OKGadgetMsg,        /* GadgetText */
  1686.     0,                   /* MutualExclude */
  1687.     NULL,                /* SpecialInfo */
  1688.     99,                  /* GadgetID */
  1689.     NULL                 /* UserData */
  1690.   };
  1691.   
  1692.   static SHORT BdrCoords[10] = { 2,2, 0,2, 0,57, 2,57, 2,2 };
  1693.                                               /* 0's to be replaced later */
  1694.   static struct Border Bdr = {
  1695.     0, 0,           /* LeftEdge, TopEdge */
  1696.     BDRCOLOR, BGCOLOR, JAM1,   /* FrontPen, BackPen, DrawMode */
  1697.     5,              /* Count */
  1698.     &BdrCoords[0],  /* XY */
  1699.     NULL            /* NextBorder */
  1700.   };
  1701.  
  1702.   static struct IntuiText ErrText = {
  1703.     TEXTCOLOR, BGCOLOR, JAM1,   /* FrontPen, BackPen, DrawMode */
  1704.     8, 18,             /* LeftEdge, TopEdge */
  1705.     NULL,              /* ITextFont */
  1706.     NULL,              /* IText -- Will be filled later */
  1707.     NULL,              /* NextText */
  1708.   };
  1709.  
  1710.   static struct Image ErrWinBgnd = {    /* Just a filled rectangle */
  1711.     0, 0,              /* LeftEdge, TopEdge */
  1712.     0, 60, 0,          /* Width,Height, Depth */
  1713.     NULL,              /* ImageData */
  1714.     0x0, 0xE,          /* PlanePick, PlaneOnOff */
  1715.     NULL               /* NextImage */
  1716.   };
  1717.  
  1718.   static struct NewWindow ErrNewWindow = {
  1719.     0,60,   /* window XY origin relative to TopLeft of screen */
  1720.     0,60,    /* window width and height */
  1721.     TEXTCOLOR,BDRCOLOR,  /* detail and block pens */
  1722.     GADGETUP,  /* IDCMP flags */
  1723.     ACTIVATE|SIMPLE_REFRESH|BORDERLESS,  /* other window flags */
  1724.     &OKGadget,   /* first gadget in gadget list */
  1725.     NULL,   /* custom CHECKMARK imagery */
  1726.     NULL,   /* window title */
  1727.     NULL,   /* custom screen pointer */
  1728.     NULL,   /* custom bitmap */
  1729.     5,5,    /* minimum width and height */
  1730.     -1,-1,  /* maximum width and height */
  1731.     CUSTOMSCREEN  /* destination screen type */
  1732.   };
  1733.  
  1734.   /*** Compute and fill in text and window-dependent stuff */
  1735.  
  1736.   ErrNewWindow.Screen = Screen;
  1737.   ErrNewWindow.Width = strlen(text) * 8 + 16;
  1738.   if (ErrNewWindow.Width < 44)
  1739.     ErrNewWindow.Width = 44;
  1740.   ErrWinBgnd.Width = ErrNewWindow.Width;
  1741.  
  1742.   ErrNewWindow.LeftEdge = (320 - ErrNewWindow.Width)/2;
  1743.   if (ErrNewWindow.LeftEdge < 0)
  1744.     ErrNewWindow.LeftEdge = 0;
  1745.  
  1746.   BdrCoords[2] = BdrCoords[4] = ErrNewWindow.Width - 3;
  1747.  
  1748.   ErrText.IText = (UBYTE*)text;
  1749.  
  1750.   if (Window->WScreen->Width = 320) /* If Lo-res -- remove vert. double line */
  1751.     OKGadgetBdr.Count = 5;
  1752.   else
  1753.     OKGadgetBdr.Count = 9;
  1754.  
  1755.   /*** Display the Error-Window */
  1756.  
  1757.  
  1758.   if ((ErrWindow = OpenWindow(&ErrNewWindow)) == NULL) {
  1759.     printf("\nCouldn't open Error Message window!!!\n");
  1760.     CloseAll();
  1761.   }
  1762.   DrawImage(ErrWindow->RPort, &ErrWinBgnd, 0L, 0L);
  1763.   PrintIText(ErrWindow->RPort, &ErrText, 0L, 0L);
  1764.   DrawBorder(ErrWindow->RPort, &Bdr, 0L, 0L);
  1765.   RefreshGadgets(&OKGadget, ErrWindow, NULL);
  1766.                      /* Because DrawImage() obscured the OK Gadget image */
  1767.  
  1768.   /*** Wait until user clicks the Error-window's "OK" Gadget */
  1769.   gadgetid = 9999;
  1770.   while (gadgetid != 99) {
  1771.     if (message = (struct IntuiMessage *)GetMsg(ErrWindow->UserPort)) { 
  1772.       gadgetid = ((struct Gadget *)(message->IAddress))->GadgetID;
  1773.       ReplyMsg(message);
  1774.     }
  1775.   }
  1776.   CloseWindow(ErrWindow);
  1777. }
  1778. /*===========================================================================*/
  1779.  
  1780. OpenImageScreen(width, height, depth)  /* Open a Custom screen & window for */
  1781.                                        /* the Image; Set exists_image flag */
  1782. int width, height, depth;
  1783.   struct NewScreen ns;
  1784.   struct Screen *OpenScreen();
  1785.   struct NewWindow nw;
  1786.   struct Window *OpenWindow();
  1787.   long numcols;  /* # of colors in screen */
  1788.   UWORD *colortableptr[5];   /* Array of Pointers to screen colortables */
  1789.                              /* Note: element 0 is not used! */
  1790.  
  1791.   static UWORD colortable1[] =     /*** Colors for 1-bitplane screen */
  1792.     {0x000, 0x068, 0x0AF, 0x5CF};
  1793.   static UWORD colortable2[] =     /*** Colors for 2-bitplane screen */
  1794.     {0x000, 0x057, 0x07B, 0xAF};
  1795.   static UWORD colortable3[] =     /*** Colors for 3-bitplane screen */
  1796.     {0x000, 0x034, 0x046, 0x068, 0x06A, 0x08C, 0x09E, 0x0AF};
  1797.   static UWORD colortable4[] =     /*** Colors for 4-bitplane screen */
  1798.     {0x000, 0x011, 0x012, 0x023, 0x034, 0x045, 0x046, 0x057,
  1799.      0x068, 0x069, 0x06A, 0x07B, 0x08C, 0x08D, 0x09E, 0x0AF};
  1800.  
  1801.   colortableptr[1] = colortable1; 
  1802.   colortableptr[2] = colortable2; 
  1803.   colortableptr[3] = colortable3; 
  1804.   colortableptr[4] = colortable4;
  1805.  
  1806.   ns.LeftEdge = 0;      /*** Initialize NewScreen structure */
  1807.   ns.TopEdge = 0;
  1808.   ns.Width = width;
  1809.   ns.Height = height;
  1810.   ns.Depth = (depth == 1) ? 2 : depth;
  1811.   ns.DetailPen = 3;  /* Colour of text in screen title bar */
  1812.   ns.BlockPen = 1;   /* Colour of screen Title Bar */
  1813.   ns.ViewModes = ((width == 640) ? HIRES:0) | ((height == 400) ? INTERLACE:0);
  1814.   ns.Type = CUSTOMSCREEN;
  1815.   ns.Font = &TOPAZ80;
  1816.   ns.DefaultTitle = (UBYTE*)" Click in screen to stop rendering";
  1817.   ns.Gadgets = NULL;
  1818.   ns.CustomBitMap = NULL;
  1819.  
  1820.   switch (depth) {    /* Customize Image Title and Border colors to depth */
  1821.   case (1):
  1822.     ns.DetailPen = 3;
  1823.     ns.BlockPen = 1;
  1824.     break;
  1825.   case (2):
  1826.     ns.DetailPen = 3;
  1827.     ns.BlockPen = 1;
  1828.     break;
  1829.   case (3):
  1830.     ns.DetailPen = 7;
  1831.     ns.BlockPen = 3;
  1832.     break;
  1833.   case (4):
  1834.     ns.DetailPen = 15;
  1835.     ns.BlockPen = 7;
  1836.     break;
  1837.   }
  1838.  
  1839.   nw.LeftEdge = 0;         /*** Initialize NewWindow structure */
  1840.   nw.TopEdge = 0;
  1841.   nw.Width = width;
  1842.   nw.Height = height;
  1843.   nw.DetailPen = ns.DetailPen; /* Title text color */
  1844.   nw.BlockPen  = ns.BlockPen;  /* Title bar background */
  1845.   nw.IDCMPFlags = MOUSEBUTTONS;
  1846.   nw.Flags = ACTIVATE|GIMMEZEROZERO|SIMPLE_REFRESH|BACKDROP;
  1847.   nw.FirstGadget = NULL;
  1848.   nw.CheckMark = NULL;
  1849.   nw.Title = (UBYTE*)" ";   /* Never seen - stays behind screen title bar */
  1850.   nw.Screen = NULL; /* Will set it to ImageScreen after the screen is opened */
  1851.   nw.BitMap = NULL;
  1852.   nw.MinWidth = 0;
  1853.   nw.MinHeight = 0;
  1854.   nw.MaxWidth = 0;
  1855.   nw.MaxHeight = 0;
  1856.   nw.Type = CUSTOMSCREEN;
  1857.  
  1858.   /*** Open the screen */
  1859.   if ((ImageScreen = (struct Screen *) OpenScreen(&ns)) == NULL) {
  1860.     ShowError("Couldn't open Image screen!");
  1861.     return;
  1862.   }
  1863.  
  1864.   /* Link Custom colours defined above to screen's ViewPort */  
  1865.   numcols = 1 << ns.Depth;
  1866.   LoadRGB4(&(ImageScreen->ViewPort), colortableptr[depth], numcols);
  1867.  
  1868.   /*** Open the Window */
  1869.   nw.Screen = ImageScreen;
  1870.   if ((ImageWindow = (struct Window *) OpenWindow(&nw)) == NULL) {
  1871.     CloseScreen(ImageScreen);
  1872.     ShowError("Couldn't open Image window!");
  1873.     return;
  1874.   }
  1875.   SetWindowTitles(ImageWindow,-1L,
  1876.                               (char *)" Click in screen to stop rendering");
  1877.   exists_image = 1;
  1878. }
  1879. /*===========================================================================*/
  1880.  
  1881. CloseImageScreen()   /* Close image screen & Window, reset exists_image flag */
  1882. {
  1883.   struct Message * GetMsg();
  1884.   void ReplyMsg();
  1885.   struct IntuiMessage *message;
  1886.  
  1887.   /* Empty ImageWindow IDCMP queue if any */
  1888.   while (message = (struct IntuiMessage *)GetMsg(ImageWindow->UserPort))
  1889.       ReplyMsg(message);
  1890.  
  1891.   CloseWindow(ImageWindow);
  1892.   CloseScreen(ImageScreen);
  1893.   exists_image = 0;
  1894. }
  1895. /*===========================================================================*/
  1896.  
  1897. DoDemo()        /* Demonstrate IFS process, then NEW and return */
  1898. {
  1899.   if (SetMode(OUTLINE) == 1) {
  1900.     SetRast(r, 0L);
  1901.     exists_outline = 0;
  1902.   }
  1903.   else   /* User clicked CANCEL in IFS WILL BE LOST requester - Abort demo */
  1904.     return;
  1905.   ModifyIDCMP(Window, NULL);   /* Disable Editor Window IDCMP during Demo */
  1906.   ClearMenuStrip(Window); /* remove main window menus during demo */
  1907.   AboutText(DEMOTEXT);    /* Show explanation of demo in text screen */
  1908.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  1909.  
  1910.   /* Draw outline of Sierpinski Triangle */
  1911.   SetDrMd(r, JAM1);
  1912.   for (i = 0; i < 100000; i++) ; /* Delay */
  1913.   Move(r, 138L, 24L);
  1914.   Draw(r, 24L, 172L);
  1915.   for (i = 0; i < 100000; i++) ; /* Delay */
  1916.   Draw(r, 238L, 131L);
  1917.   for (i = 0; i < 100000; i++) ; /* Delay */
  1918.   Draw(r, 138L, 24L);
  1919.   for (i = 0; i < 100000; i++) ; /* Delay */
  1920.   Move(r, 82L, 98L);
  1921.   Draw(r, 188L, 77L);
  1922.   for (i = 0; i < 100000; i++) ; /* Delay */
  1923.   Draw(r, 130L, 152L);
  1924.   for (i = 0; i < 100000; i++) ; /* Delay */
  1925.   Draw(r, 82L, 98L);
  1926.   exists_outline = 1;
  1927.   for (i = 0; i < 150000; i++)  /* Delay before entering Collage Editor */
  1928.     ;
  1929.  
  1930.   /*** Place 3 Pieces to form Collage */
  1931.  
  1932.   SetMode(COLLAGE);
  1933.   for (i = 0; i < 100000; i++) ; /* Delay */
  1934.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  1935.   AddDefPiece();
  1936.   DrawCollage();
  1937.   pieceptr[0]->e = 0.25;
  1938.   pieceptr[0]->f = 0.045;
  1939.   pieceptr[0]->dens = 1.0;
  1940.   ComputeBoxCorners(pieceptr[0]);
  1941.   ComputeProbs();
  1942.   for (i = 0; i < 100000; i++) ; /* Delay */
  1943.   TransformPiece(outlinebufptr, pieceptr[0]->piecemap, pieceptr[0]);
  1944.   DrawCollage();
  1945.  
  1946.   for (i = 0; i < 100000; i++) ; /* Delay */
  1947.   AddDefPiece();
  1948.   DrawCollage();
  1949.   pieceptr[1]->e = 0.045;
  1950.   pieceptr[1]->f = 0.315;
  1951.   pieceptr[1]->dens = 0.4;
  1952.   ComputeBoxCorners(pieceptr[1]);
  1953.   ComputeProbs();
  1954.   for (i = 0; i < 100000; i++) ; /* Delay */
  1955.   TransformPiece(outlinebufptr, pieceptr[1]->piecemap, pieceptr[1]);
  1956.   DrawCollage();
  1957.  
  1958.   for (i = 0; i < 100000; i++) ; /* Delay */
  1959.   AddDefPiece();
  1960.   DrawCollage();
  1961.   pieceptr[2]->e = 0.43;
  1962.   pieceptr[2]->f = 0.24;   
  1963.   pieceptr[2]->dens = 0.7;
  1964.   ComputeBoxCorners(pieceptr[2]);
  1965.   ComputeProbs();
  1966.   for (i = 0; i < 100000; i++) ; /* Delay */
  1967.   TransformPiece(outlinebufptr, pieceptr[2]->piecemap, pieceptr[2]);
  1968.   DrawCollage();
  1969.  
  1970.   for (i = 0; i < 300000; i++)  /* Delay before rendering */
  1971.     ;
  1972.   RenderImage(0);               /* Render, until user clicks in image screen */
  1973.  
  1974.   /*** After rendering ended, do a NEW (without IFS WILL BE LOST requester) */
  1975.   ClearIFS();
  1976.   RemoveGList(Window, &CollageGadgetList2, 6L);
  1977.   AddGList(Window, &GadgetList1, 0L, 6L, NULL); /* change gadgets */
  1978.   SetDrMd(scrp, JAM1);
  1979.   SetAPen(scrp, 0L);
  1980.   RectFill(scrp, 280L, 12L, 316L, 117L); /* Blot out old gadget images */
  1981.   RefreshGadgets(&GadgetList1, Window, NULL);
  1982.   OffMenu(Window, NOITEM<<5 | 2);     /* disable render menu and its items */
  1983.   SetWindowTitles(Window, -1L, "           Outline Editor");
  1984.   drawmode = FREEHAND;
  1985.   HiliteGadget(0);
  1986.   SetAPen(r, 2L);
  1987.   SetRast(r, 0L);
  1988.   exists_outline = 0;
  1989.   CloseImageScreen();
  1990.   ModifyIDCMP(Window, MOUSEBUTTONS|GADGETUP|MENUPICK);  /* Restore IDCMP */
  1991.   SetMenuStrip(Window, &MenuList1);  /* Restore menu strip */
  1992. }
  1993. /*===========================================================================*/
  1994.  
  1995. ComputeBoxCorners(pcptr)     /* Compute Vector Box corners from Piece coeffs */
  1996. struct piece *pcptr;
  1997. {
  1998.   pcptr->boxo.x = pcptr->e * GZZWIDTH;
  1999.   pcptr->boxo.y = pcptr->f * GZZWIDTH; /* sic! */
  2000.   pcptr->boxx.x = (pcptr->a + pcptr->e) * GZZWIDTH;
  2001.   pcptr->boxx.y = (pcptr->c + pcptr->f) * GZZWIDTH;
  2002.   pcptr->boxy.x = (pcptr->b*GZZHEIGHT/GZZWIDTH + pcptr->e) * GZZWIDTH;
  2003.   pcptr->boxy.y = (pcptr->d*GZZHEIGHT/GZZWIDTH + pcptr->f) * GZZWIDTH;
  2004.   pcptr->boxz.x =  pcptr->boxy.x + pcptr->boxx.x - pcptr->boxo.x;
  2005.   pcptr->boxz.y = pcptr->boxy.y + pcptr->boxx.y - pcptr->boxo.y;
  2006. }
  2007. /*===========================================================================*/
  2008.  
  2009. LoadILBM(fnam, dest)        /* Load an Image from IFF file; If dest = 1,    */
  2010. char *fnam;                 /* put image and colormap in Image Screen;      */
  2011. int dest;                   /* if dest = 0, put plane 0 of image in Outline */
  2012. {
  2013.   struct BitMap picbitmap = {0};    /* Empty BitMap structure to "Fill" */
  2014.   ILBMFrame iFrame;         /* ILBM Frame to be used by Reader Routines */
  2015.   LONG file;  /* File handle [we use AmigaDOS Open(), not Manx open()] */
  2016.   LONG iffp;
  2017.   char *blitbuffer; /* Needed for the Blit operation only */
  2018.   struct BitMap tmpbitmap;  /* used for 2-step blit to Outline */
  2019.  
  2020.   #define MIN(a,b) ((a)<(b)?(a):(b))
  2021.  
  2022.   /* Open the File to read */
  2023.   if ((file = Open(fnam, MODE_OLDFILE)) == NULL) {
  2024.     ShowError("Can't open file!");
  2025.     return;
  2026.   }
  2027.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  2028.  
  2029.   /* Use EA IFF routine ReadPicture() to Read image from ILBM file into */
  2030.   /*  memory, modify picbitmap accordingly */
  2031.  
  2032.   iffp = ReadPicture(file,&picbitmap,&iFrame,ChipAlloc);
  2033.  
  2034.   Close(file);
  2035.  
  2036.   if (iffp != IFF_DONE) {  /* React to error message from EA routine, if any */
  2037.     ShowError(IFFPMessages[-iffp]);
  2038.     if (picbitmap.Planes[0]) /* free planes allocated by ReadPicture, if any */
  2039.       RemFree(picbitmap.Planes[0]);
  2040.     ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2041.     return;
  2042.   }
  2043.  
  2044.   /*** Now image is in memory; copy it to the proper destination bitmap */
  2045.  
  2046.   switch (dest) {
  2047.  
  2048.   case 0:  /***  Copy plane 0 of loaded image to Outline (plane 1 in Window) */
  2049.  
  2050.     SetRast(r, 0L);   /* Erase old outline, set drawmode to FREEHAND */
  2051.     if(drawmode == ERASE || drawmode == FILL) {
  2052.       drawmode = FREEHAND;
  2053.       SetAPen(r,2L);
  2054.       HiliteGadget(0);
  2055.     }
  2056.  
  2057.     /* NOTE: Must use 2 steps because must blit (possibly part of) loaded */
  2058.     /*  image of arbitrary size, adjusting for GZZ borders, and from plane 0 */
  2059.     /*  to plane 1. Neither BltBitMap nor BltTemplate can do this in 1 step */
  2060.  
  2061.     /* Step 1: Blit plane 0 of loaded image to temp bitmap  */
  2062.     /*         (uses outline buffer as its single bitplane) */
  2063.     InitBitMap(&tmpbitmap, 1L, WIDTH, HEIGHT);
  2064.     tmpbitmap.Planes[0] = (PLANEPTR)outlinebufptr;
  2065.  
  2066.     blitbuffer = (char *)AllocMem(80L,MEMF_CHIP); /* Allocate temp buffer */
  2067.     BltBitMap(&picbitmap, 0L, 0L, &tmpbitmap, 0L, 0L,
  2068.      MIN(WIDTH, picbitmap.BytesPerRow*8L),
  2069.      MIN(HEIGHT, picbitmap.Rows), 192L, 0x1, blitbuffer);
  2070.      /* 192L is minterm for Direct Copy; 0x1 is mask, will copy plane 0 */
  2071.     FreeMem(blitbuffer, 80L);
  2072.  
  2073.     /* Step 2: Blit the temporary bitplane from Outline Buffer to plane 1 */
  2074.     /*         of GZZ Window */
  2075.  
  2076.     SetAPen(r, 2L);
  2077.     SetDrMd(r, JAM1);
  2078.     SetWrMsk(r,0x0002);  /* Write protect all planes except plane 1 */
  2079.     BltTemplate((char*)outlinebufptr, 0L, WIDTH/8L, Window->RPort, 0L, 0L, 
  2080.      MIN(GZZWIDTH, picbitmap.BytesPerRow*8L), MIN(GZZHEIGHT, picbitmap.Rows));
  2081.     SetWrMsk(r,0xFFFF);
  2082.  
  2083.     exists_outline = 1;
  2084.     break;
  2085.  
  2086.   case 1:   /***  Set up a (new) Image Screen and blit loaded image into it */
  2087.     if (exists_image)
  2088.       CloseImageScreen();
  2089.     OpenImageScreen(iFrame.bmHdr.pageWidth, iFrame.bmHdr.pageHeight,
  2090.                                                             picbitmap.Depth);
  2091.  
  2092.     /* Link colours read in from file to screen's ViewPort */  
  2093.     LoadRGB4(&(ImageScreen->ViewPort), iFrame.colorMap, 1<<picbitmap.Depth);
  2094.  
  2095.     SetWindowTitles(ImageWindow,
  2096.                (char *)" ", (char *)" Click in screen to push it back");
  2097.     blitbuffer = (char *)AllocMem(80L,MEMF_CHIP); /* Allocate temp buffer */
  2098.     BltBitMap(&picbitmap, 2L, 11L, ImageWindow->RPort->BitMap, 2L, 11L,
  2099.      MIN(ImageWindow->Width-4L,picbitmap.BytesPerRow*8L-2L),
  2100.      MIN(ImageWindow->Height-13L,picbitmap.Rows-11L), 192L, 0xFF, blitbuffer);
  2101.      /* 192L is minterm for Direct Copy; 0xFF is mask, will copy all planes */
  2102.      /* 2L, 11L, 4L, 13L cause The blit to skip the window borders */
  2103.     FreeMem(blitbuffer, 80L);
  2104.     exists_image = 1;
  2105.     break;
  2106.   }       /* End switch dest */
  2107.  
  2108.   if (picbitmap.Planes[0])  /* free planes allocated by ReadPicture, if any */
  2109.     RemFree(picbitmap.Planes[0]);
  2110.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2111. }
  2112. /*==========================================================================*/
  2113.  
  2114. AboutText(whichtext)   /* Opens the text screen and displays in it */
  2115. int whichtext;         /* the selected About text or IFS code table */
  2116.                        /* according to whichtext. Closes on click in screen */
  2117.        /* whichtext can be: CODES, IFS_THEORY, HELP1, HELP2, AUTHOR, DEMOTEXT*/
  2118. {
  2119.   struct Screen *TextScreen;
  2120.   struct Window *TextWindow;
  2121.   struct NewScreen ns;
  2122.   struct Screen *OpenScreen();
  2123.   struct NewWindow nw;
  2124.   struct Window *OpenWindow();
  2125.   struct Message * GetMsg();
  2126.   struct IntuiMessage *message;
  2127.   static struct IntuiText RedText = {
  2128.     3, 0, JAM1,        /* FrontPen, BackPen, DrawMode */
  2129.     0, 0,              /* LeftEdge, TopEdge */
  2130.     &TOPAZ80,          /* ITextFont */
  2131.     (UBYTE*)"Click Mouse to Continue", NULL  /* IText, NextText */
  2132.   };
  2133.   char codelinebuf[78];   /* Buffer for line of codes in CODES display */
  2134.   static struct IntuiText CodesText = {     /* Codes line for display */
  2135.     1, 0, JAM1,        /* FrontPen, BackPen, DrawMode */
  2136.     0, 0,              /* LeftEdge, TopEdge */
  2137.     &TOPAZ80,          /* ITextFont */
  2138.     NULL, NULL  /* IText, NextText */
  2139.   };
  2140.  
  2141.   /*** Open a window in a custom Hires screen for text display */
  2142.  
  2143.   ns.LeftEdge = 0;      /*** Initialize NewScreen structure */
  2144.   ns.TopEdge = 0;
  2145.   ns.Width = 640;
  2146.   ns.Height = 200;
  2147.   ns.Depth = 2;
  2148.   ns.DetailPen = 0;  /* Colour of text in screen title bar */
  2149.   ns.BlockPen = 1;   /* Colour of screen Title Bar */
  2150.   ns.ViewModes = HIRES;
  2151.   ns.Type = CUSTOMSCREEN;
  2152.   ns.Font = &TOPAZ80;
  2153.   ns.DefaultTitle = NULL;
  2154.   ns.Gadgets = NULL;
  2155.   ns.CustomBitMap = NULL;
  2156.  
  2157.   nw.LeftEdge = 0;         /*** Initialize NewWindow structure */
  2158.   nw.TopEdge = 0;
  2159.   nw.Width = 640;
  2160.   nw.Height = 200;
  2161.   nw.DetailPen = 0; /* Menu title text color */
  2162.   nw.BlockPen  = 1; /* Menu item box and title bar background */
  2163.   nw.IDCMPFlags = MOUSEBUTTONS;
  2164.   nw.Flags = ACTIVATE|SIMPLE_REFRESH;
  2165.   nw.FirstGadget = NULL;
  2166.   nw.CheckMark = NULL;
  2167.   nw.Title = (UBYTE*)" ";
  2168.   nw.Screen = NULL;  /* Will set it to TextScreen after the screen is opened */
  2169.   nw.BitMap = NULL;
  2170.   nw.MinWidth = 0;
  2171.   nw.MinHeight = 0;
  2172.   nw.MaxWidth = 0;
  2173.   nw.MaxHeight = 0;
  2174.   nw.Type = CUSTOMSCREEN;
  2175.  
  2176.   /*** Open the screen */
  2177.   if ((TextScreen = (struct Screen *) OpenScreen(&ns)) == NULL) {
  2178.     ShowError("Couldn't open Text screen!!!");
  2179.     return;
  2180.   }
  2181.  
  2182.   /*** Open the Window */
  2183.   nw.Screen = TextScreen;
  2184.   if ((TextWindow = (struct Window *) OpenWindow(&nw)) == NULL) {
  2185.     CloseScreen(TextScreen);
  2186.     ShowError("Couldn't open Text window!!!");
  2187.     return;
  2188.   }  
  2189.   Textrp = (TextWindow->RPort);  /* Window's rastport, used by p() */
  2190.  
  2191.   p(NULL);  /* reset screen print routine to top of screen */
  2192.   p(" ");   /* skip 2 lines to clear text window title bar */
  2193.   p(" ");
  2194.  
  2195.   switch (whichtext) {   /**** Print appropreiate text into window */
  2196.  
  2197.   case IFS_THEORY:
  2198.     SetWindowTitles(TextWindow,  " IFS Theory Background", -1L);
  2199.  
  2200. p("   IFS is a method developed by Michael F. Barnsley that allows one to en\
  2201. code a");
  2202. p(" Fractal as a small set of numbers, the coefficients of an Iterated Funct\
  2203. ion");
  2204. p(" System (IFS) Code, and later to reconstruct it from those numbers. An IF\
  2205. S Code");
  2206. p(" is a set of Contractive Affine Transformations, which are transformation\
  2207. s of");
  2208. p(" the plane that combine a Linear Transformation (consisting of combinatio\
  2209. ns of");
  2210. p(" stretching and rotation) and a Translation of the origin. Each such affi\
  2211. ne");
  2212. p(" transformation can be represented by six real numbers. In addition, with\
  2213.  each");
  2214. p(" transformation is associated a probability value.");
  2215. p(" ");
  2216. p("   To derive the IFS Code for a given Fractal image, one must find a set \
  2217. of");
  2218. p(" transformations of the image - reduced, deformed copies of it - that whe\
  2219. n");
  2220. p(" taken together cover the original image. To reconstruct  the image, one \
  2221. starts");
  2222. p(" with any point in the plane,  picks at random one of the transformations");
  2223. p(" defined above, applies it to the point, and draws the resulting point. T\
  2224. o this");
  2225. p(" new point one applies the same procedure, and so on. The probabilities d\
  2226. efine");
  2227. p(" how often each transformation will be picked. The 'Collage Theorem' assu\
  2228. res");
  2229. p(" that this process will in fact reconstruct the original Fractal.");
  2230. p(" ");
  2231. p("   For more detailed information on the theory and use of IFS, refer to:");
  2232. p("    - Barnsley and Sloan: A Better Way to Compress Images, BYTE, Jan. 88\
  2233. .");
  2234. p("    - Peitgen and Saupe, eds.: The Science of Fractal Images, Chapter 5.");
  2235.  
  2236.     break;
  2237.  
  2238.   case HELP1:
  2239.     SetWindowTitles(TextWindow,  " A Summary of Program Operation", -1L);
  2240.  
  2241. p(" This program requires two steps to generate an IFS Code for a Fractal:");
  2242. p(" ");
  2243. p(" a. OUTLINE EDITOR: This step allows you to draw the approximate Outline \
  2244. of");
  2245. p("  the planned Fractal. The user interface is a simple Paint program, with");
  2246. p("  Freestyle, Lines, Erase (XXX), Fill and Clear gadgets. Note that the le\
  2247. ss");
  2248. p("  pixels you draw, the faster things will move later, in the Collage Edit\
  2249. or");
  2250. p("  phase. When your Outline is finished, click DONE to enter the Collage E\
  2251. ditor.");
  2252. p(" ");
  2253. p(" b. COLLAGE EDITOR: Here you create the Collage, by defining the affine");
  2254. p("  transformations ('Pieces') to cover the Outline with. When you enter th\
  2255. is");
  2256. p("  mode, you see your Outline, and one Default transformation - a half-siz\
  2257. ed");
  2258. p("  copy of this Outline, drawn in red. The box around it is the image of t\
  2259. he");
  2260. p("  screen boundary under the transformation. You can modify the Piece by");
  2261. p("  dragging any corner of this box with the mouse. The corner marked 'O' w\
  2262. ill");
  2263. p("  translate the Origin; 'X' and 'Y' will stretch and rotate these axes; T\
  2264. he");
  2265. p("  last corner will enlarge and rotate the Piece without deforming it. Cli\
  2266. ck");
  2267. p("  ADD to add a new Default Piece, or DUP to add a duplicate of the curren\
  2268. t");
  2269. p("  Piece. The Piece drawn in red is the 'Selected' one; all editing action\
  2270. s");
  2271. p("  apply to it. Clicking SEL repeatedly will make one Piece after another \
  2272. the");
  2273. p("  Selected one, allowing you to modify (or delete, by clicking DEL) previ\
  2274. ously");
  2275. p("  defined Pieces.");
  2276.  
  2277.     break;
  2278.  
  2279.   case HELP2:
  2280.     SetWindowTitles(TextWindow, " A Summary of Program Operation (Continued)",
  2281.                                                                           -1L);
  2282.  
  2283. p("    You can click the NUM gadget to get a numeric display of the Selected");
  2284. p("  transformation's coefficients (a-f, p), as well as the associated Proba\
  2285. bility");
  2286. p("  Density (probability per unit area) and Scalings, Rotations and Transla\
  2287. tions.");
  2288. p("  You can modify any one of these values in their string gadgets, and cli\
  2289. ck");
  2290. p("  ENTER to make the changes take effect. To quit the numeric window, clic\
  2291. k its");
  2292. p("  Close gadget.");
  2293. p(" ");
  2294. p("    When you've covered the Outline fully with Pieces, use the Render men\
  2295. u");
  2296. p("  to generate the Attractor of the IFS in the resolution and gray level c\
  2297. ount");
  2298. p("  of your choice.");
  2299. p(" ");
  2300. p("    The Optimize menu item will modify the current IFS so that its Attrac\
  2301. tor");
  2302. p("  will fill the screen; it will also generate an exact Collage for it, by\
  2303.  using");
  2304. p("  the Attractor itself as an Outline.");
  2305. p(" ");
  2306. p("    File I/O menuitems allow you to save the IFS codes or the Image to d\
  2307. isk,");
  2308. p("  to load IFS Codes from files saved by IFSLab and by most other IFS prog\
  2309. rams,");
  2310. p("  and to load any Non-HAM IFF image file into the Image screen or as the");
  2311. p("  Outline.");
  2312.     break;
  2313.  
  2314.   case AUTHOR:
  2315.     SetWindowTitles(TextWindow,  "  Author Information", -1L);
  2316. p(" ");
  2317. p(" ");
  2318. p(" ");
  2319. p(" ");
  2320. p("                       IFS Lab written by Nathan Zeldes");
  2321. p(" ");
  2322. p(" ");
  2323. p("              Copyright (C) 1992 by N. Zeldes. All Rights Reserved. ");
  2324. p(" ");
  2325. p("           Thanks to C.W. Fox and Bruce Dawson for the File Requester");
  2326. p(" ");
  2327. p(" ");
  2328. p("                                                                       ");
  2329. p(" ");
  2330. p(" ");
  2331. p(" ");
  2332.  
  2333.     break;
  2334.  
  2335.   case DEMOTEXT:  
  2336.     SetWindowTitles(TextWindow,  " A Demonstration of IFS in Action", -1L);
  2337.  
  2338. p(" ");
  2339. p("   This demo will define and render the Sierpinski Triangle, a well known");
  2340. p(" fractal.");
  2341. p(" ");
  2342. p("   The program will first enter the Outline Editor and draw an Outline of\
  2343.  the");
  2344. p(" Sierpinski triangle. After a brief delay it will switch to the Collage E\
  2345. ditor");
  2346. p(" and show how the Outline can be covered by three half-sized copies of it\
  2347. self.");
  2348. p(" When it has built this Collage, the program will initiate rendering of t\
  2349. he");
  2350. p(" fractal. Note that this demo intentionally assigns unequal probabilities\
  2351.  to");
  2352. p(" the three transformations, which causes the uneven 'shading' effect in t\
  2353. he");
  2354. p(" rendered image.");
  2355. p(" ");
  2356. p("   To start the demo, click the left mouse button.  Do no more until the"); 
  2357. p(" rendering begins. When you've had your fill of the beauty of the emergin\
  2358. g");
  2359. p(" image, click twice in the Image screen to exit the demo.");
  2360.  
  2361.     break;
  2362.  
  2363.   case CODES:    /* Display Codes Table */
  2364.     SetWindowTitles(TextWindow,  " IFS Code Coefficients", -1L);
  2365.  
  2366.     p("    A      B      C      D     E     F     P     Dens   S1    S2\
  2367.     R1    R2 ");
  2368.     if (N < MAX_N)
  2369.       p(" ------ ------ ------ ------ ----- ----- ----- ------- ----- -----\
  2370.  ----- ----- ");   /* The underline is omitted to allow display of 20 pieces */
  2371.  
  2372.     for(i = 0; i <= N; i++) {    /* Put lines of coefficients in text window */
  2373.  
  2374.       sprintf(codelinebuf, " % 6.3lf % 6.3lf % 6.3lf % 6.3lf %5.3lf %5.3lf\
  2375.  %5.3lf %7.3lf% 5.3lf %5.3lf % 4.0lf  % 4.0lf", pieceptr[i]->a, pieceptr[i]->b,
  2376.        pieceptr[i]->c, pieceptr[i]->d, pieceptr[i]->e, pieceptr[i]->f,
  2377.        pieceptr[i]->p, pieceptr[i]->dens, pieceptr[i]->s1, pieceptr[i]->s2,
  2378.        pieceptr[i]->r1/0.017453293, pieceptr[i]->r2/0.017453293); 
  2379.  
  2380.       CodesText.FrontPen = (i == selpiece) ? 3 : 1; /* Selected piece in red */
  2381.       CodesText.IText = (UBYTE*)codelinebuf;
  2382.       CodesText.TopEdge = (N < MAX_N) ? 8L * i + 32L : 8L * i + 24L;
  2383.       PrintIText(Textrp, &CodesText, 0L, 0L);
  2384.     }
  2385.     break;
  2386.  
  2387.   }  /* End switch */
  2388.  
  2389.   PrintIText(Textrp, &RedText, 450L, 190L); /* 'Click Mouse...' text */
  2390.  
  2391.   /*** Wait with text displayed, poll IDCMP until you get a SELECTDOWN */
  2392.   while (!(message = (struct IntuiMessage *)GetMsg(TextWindow->UserPort))) 
  2393.     ;   /* here any event must be a SELECTDOWN due to IDCMP flag definition */
  2394.   ReplyMsg(message);
  2395.   while (!(message = (struct IntuiMessage *)GetMsg(TextWindow->UserPort))) 
  2396.     ;    /* get rid of SELECTUP message following the SELECTDOWN */
  2397.   ReplyMsg(message);
  2398.  
  2399.   CloseWindow(TextWindow);   /* Close text display and return */
  2400.   CloseScreen(TextScreen);
  2401. }
  2402. /*===========================================================================*/
  2403.  
  2404. PutDefOutline()  /* Place default Outline in Outline Bitmap */
  2405. {
  2406.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  2407.   SetDrMd(r, JAM1);
  2408.   SetAPen(r, 2L);
  2409.   SetOPen(r, 2L);
  2410.   SetWrMsk(r,0x0002);  /* Write protect all planes except plane 1 */
  2411.   SetRast(r, 0L);
  2412.  
  2413.   DrawCircle(r, 138L, 93L, 22L);   /* Face outline */
  2414.   DrawCircle(r, 138L, 93L, 13L);   /* To become mouth outline */
  2415.   SetAPen(r, 0L);
  2416.   SetOPen(r, 0L);
  2417.   RectFill(r, 124L, 80L, 152L, 100L); /* Blot out top part of mouth circle */
  2418.   SetAPen(r, 2L);
  2419.   SetOPen(r, 2L);
  2420.   DrawCircle(r, 129L, 85L, 3L);   /* Left Eye */
  2421.   DrawCircle(r, 147L, 85L, 3L);   /* Right Eye */
  2422.  
  2423.   exists_outline = 1;
  2424.   SetWrMsk(r,0xFFFF);
  2425.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2426. }
  2427. /*===========================================================================*/
  2428.  
  2429. TransformPiece(source, dest, pc)      /* Apply transformation of Piece */
  2430.      /* To image in source bitplane, put result in dest piecemap. */
  2431.      /* Return 1 if done OK, return 0 if a pixel overflowed plane boudary. */
  2432.      /* Any overflow pixels are not put into the destination piecemap. */
  2433.      /* Skips title bar and borders of source bitplane properly. */
  2434.      /* Replaces doubles with scaled-up integers for speed. */
  2435.  
  2436. UBYTE *source;      /* Pointer to 1st byte in a single Window-sized Bitplane */
  2437. UBYTE *dest;        /* Pointer to first byte in target piecemap */
  2438. struct piece *pc;   /* pointer to piece structure containing trans' coeffs */
  2439. {
  2440.   #define LEFTBDRBITS     4  /* Width in BITS of left border of Window */
  2441.   #define TOPBDRBYTES   440  /* bytes within Top Border of Window */
  2442.   #define RIGHTBDRBYTES   5  /* bytes within right Border (must be integral) */
  2443.   #define MAPROWBYTES    35  /* Number of bytes in a net row of piecemap */
  2444.   #define MAPROWBYTESPLUS 40 /* Number of bytes in a gross row of piecemap */
  2445.   #define PIECEMAPWIDTH  GZZWIDTH+Window->BorderLeft
  2446.  
  2447.   UBYTE *srcbyte;      /* address of current byte in source bitplane */
  2448.   int bitpos;     /* bit position in byte (leftmost bit = 0) */
  2449.   int rowbyte;   /* number of current source byte within row */
  2450.   int srcx, srcy, destx, desty;  /* Source and destination pixel X-Y coords */
  2451.   long ai, bi, ci, di, ei, fi; /* Scaled integer transformation coefficients */
  2452.   long lbdri;   /* Scaled LEFTBDRBITS */
  2453.   int overflowflag = 0;
  2454.  
  2455.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  2456.   /* Erase piecemap */
  2457.   BltClear((char*)dest, (long)RASSIZE(WIDTH,GZZHEIGHT), 1L);
  2458.  
  2459.   /*** Convert coefficients to scaled integers */
  2460.  
  2461.   ai = (long)(pc->a * 1000000);
  2462.   bi = (long)(pc->b * 1000000);
  2463.   ci = (long)(pc->c * 1000000);
  2464.   di = (long)(pc->d * 1000000);
  2465.   ei = (long)(pc->e * 1000000 * GZZWIDTH);
  2466.   fi = (long)(pc->f * 1000000 * GZZWIDTH);
  2467.   lbdri = (long)(LEFTBDRBITS * 1000000);
  2468.  
  2469.   srcbyte = source + TOPBDRBYTES;  /* Initialize srcbyte to after Top Border */
  2470.  
  2471.   for (srcy = 0; srcy < GZZHEIGHT; srcy++) {   /* Loop on source plane rows */
  2472.     /* Loop on bytes in source row - up to right border only */
  2473.     for (rowbyte = 0; rowbyte < MAPROWBYTES; rowbyte++) {
  2474.  
  2475.       if (*srcbyte != 0) {    /* If Source byte not empty */
  2476.  
  2477.         for(bitpos = 0; bitpos < 8; bitpos++) { /* loop on bits in byte */
  2478.           if ((*srcbyte & (128>>bitpos)) != 0) { /* current src pixel is '1' */
  2479.  
  2480.             srcx = rowbyte * 8 + bitpos - LEFTBDRBITS;
  2481.  
  2482.             /* do transformation */
  2483.             destx = (ai * srcx + bi * srcy + ei + lbdri) / 1000000;
  2484.             desty = (ci * srcx + di * srcy + fi) / 1000000;
  2485.  
  2486.             if(destx < LEFTBDRBITS || destx >= PIECEMAPWIDTH ||
  2487.              desty < 0 || desty >= GZZHEIGHT)     /* Don't write transformed */
  2488.               overflowflag = 1;  /* pixel to dest - it is outside  bitplane! */
  2489.             else 
  2490.               /* write '1' pixel to destination. Expression to left of |=  */
  2491.               /* is destination byte address;  128>>bitpos is the bitmask. */
  2492.               *( dest + desty*MAPROWBYTESPLUS + destx/8 ) |= 128>>(destx % 8);
  2493.           }  /* End if current source pixel is '1' */
  2494.         }    /* End loop on bits in byte */
  2495.       }      /* End If source byte not empty */
  2496.       srcbyte++;
  2497.     }        /* End loop on bytes in source row */
  2498.     srcbyte += RIGHTBDRBYTES;   /* skip right border */
  2499.   }          /* End loop on source rows */
  2500.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2501.   return(!overflowflag);
  2502. }
  2503. /*===========================================================================*/
  2504.  
  2505. SaveIFS()    /* Save IFS coefficients, if any, to a disk file */
  2506. {
  2507.   FILE *fopen(), *fp;
  2508.   char *fnam, *p;
  2509.   char *GetFileName();
  2510.   int strcmp();
  2511.   char *strcat();
  2512.   long k;
  2513.   
  2514.   if (drawmode != VECTOR) {
  2515.     ShowError("No IFS to Save!");
  2516.     return;
  2517.   }
  2518.   /*** Open disk file,deleting prev rev if any; if error, exit function */
  2519.  
  2520.   fnam = GetFileName("Save to File:");
  2521.   if (*fnam == '\0') {
  2522.     return;  /* do nothing - user selected CANCEL or no filename */
  2523.   }
  2524.  
  2525.   k = 0;      /* Set k to 1 if filename ends with .ifs */
  2526.   for (p = fnam; *p != '\0'; p++)
  2527.     if (strcmp(p, ".ifs") == 0)
  2528.       k = 1;
  2529.   if (k == 0)      /* Add .ifs to filename if needed */
  2530.     strcat(fnam, ".ifs");
  2531.  
  2532.   if ((fp = fopen(fnam, "w")) == NULL) {
  2533.     ShowError(" Cannot open file!!!");
  2534.     return;
  2535.   }
  2536.  
  2537.   /*** Save the current IFS codes to a disk file */
  2538.  
  2539.   fprintf(fp, "    A      B      C      D     E     F     P      Dens   S1\
  2540.     S2    R1    R2\n");
  2541.   for(i = 0; i <= N; i++)
  2542.       fprintf(fp, " % 6.3lf % 6.3lf % 6.3lf % 6.3lf %5.3lf %5.3lf\
  2543.  %5.3lf %7.3lf% 5.3lf %5.3lf % 4.0lf  % 4.0lf\n", pieceptr[i]->a,
  2544.        pieceptr[i]->b, pieceptr[i]->c, pieceptr[i]->d, pieceptr[i]->e,
  2545.        pieceptr[i]->f, pieceptr[i]->p, pieceptr[i]->dens, pieceptr[i]->s1,
  2546.        pieceptr[i]->s2, pieceptr[i]->r1/0.017453293,
  2547.        pieceptr[i]->r2/0.017453293); 
  2548.  
  2549.   fclose(fp);         /* close disk file */
  2550. }
  2551. /*===========================================================================*/
  2552.  
  2553. int LoadIFS(fnam)   /* Load IFS Code variables from disk file */
  2554. char *fnam;         /* Returns 1 if done OK, 0 if failed to load */
  2555.                     /* Skips lines of wrong form (e.g. text headers) */
  2556.                     /* and ignores excess info on a line (e.g. Geom. Coeffs) */
  2557.                     /* Calls Optimize() to create a Collage and assure */
  2558. {                   /* Attractor will not overflow window */
  2559.   FILE *fopen(), *fp;
  2560.   int c, eofread, scan;
  2561.   char linebuf[161];  /* Input lines will be truncated after 160 chars */
  2562.   double mindens;
  2563.   long xmin, xmax, ymin, ymax;
  2564.            /* GZZ PIXEL coords of boundary rect of old (as-loaded) attractor*/
  2565.   int j;
  2566.  
  2567.   /*** Open disk file for reading */
  2568.   if ((fp = fopen(fnam,"r")) == NULL) {
  2569.     ShowError(" Cannot open file!!!");
  2570.     return(0);
  2571.   }
  2572.  
  2573.   ClearIFS();   /* Erase old IFS (if any), set N to -1 */
  2574.  
  2575.   /*** Load the IFS codes line by line */
  2576.  
  2577.   eofread = 0;
  2578.   while (N < MAX_N && !eofread) {  /* Loop on lines in file */
  2579.  
  2580.     if ((pieceptr[++N] = AllocPiece()) == NULL) {  /* Allocate Piece */
  2581.       N--;         /* if couldn't allocate piece */
  2582.       ShowError("Couldn't allocate memory!");
  2583.       if (N == -1) {    /* No memory for even one Piece - Quit program */
  2584.         ShowError("Re-run with more free memory!");
  2585.         CloseAll();
  2586.       }
  2587.       ShowError("        Aborting Load!         ");
  2588.       fclose(fp);
  2589.       return(0);
  2590.     }
  2591.  
  2592.     /*** Read in a single line from file into memory, then sscanf it */
  2593.     /* (Don't use fscanf from file because it ignores end of line */
  2594.     for (i = 0; i < 160 && (c = getc(fp)) != EOF && c != '\n'; i++)
  2595.       linebuf[i] = c;
  2596.     linebuf[i] = '\0';
  2597.     if (c != EOF && c != '\n')  /* Line has been truncated after 160 chars */
  2598.       while ((c = getc(fp)) != '\n' && c != EOF)
  2599.         ;                   /* Remove any other stuff on the file line */ 
  2600.     if (c == EOF)
  2601.       eofread = 1;
  2602.    
  2603.     /* Now a null-terminated line is in linebuf & the file is positioned at */
  2604.     /* start of next file line or past EOF;eofread is set if EOF was reached */
  2605.  
  2606.     scan = sscanf(linebuf, "%lf %lf %lf %lf %lf %lf %lf", &pieceptr[N]->a,
  2607.             &pieceptr[N]->b, &pieceptr[N]->c, &pieceptr[N]->d, &pieceptr[N]->e,
  2608.             &pieceptr[N]->f, &pieceptr[N]->p);
  2609.  
  2610.     if (scan == 7) {   /* converted successfully 7 coeffs of one IFS */
  2611.  
  2612.       /*** Check that coefficients a - d and p are in legal ranges */
  2613.       if (pieceptr[N]->a < -1. || pieceptr[N]->a > 1. || pieceptr[N]->b < -1.||
  2614.        pieceptr[N]->b > 1. || pieceptr[N]->c < -1. || pieceptr[N]->c > 1. ||
  2615.        pieceptr[N]->d < -1. || pieceptr[N]->d > 1. || 
  2616.        pieceptr[N]->p <= 0. || pieceptr[N]->p > 1.) {
  2617.         ShowError("Coefficient out of Range!");
  2618.         ShowError("      Aborting Load!     ");
  2619.         fclose(fp);
  2620.         return(0);
  2621.       }
  2622.  
  2623.       /*** compute the geometrical coeffs from matrix coeffs read from file */
  2624.       pieceptr[N]->s1 = sqrt(pieceptr[N]->a*pieceptr[N]->a +
  2625.                                             pieceptr[N]->c*pieceptr[N]->c);
  2626.       pieceptr[N]->s2 = sqrt(pieceptr[N]->b*pieceptr[N]->b +
  2627.                                             pieceptr[N]->d*pieceptr[N]->d);
  2628.       pieceptr[N]->r1 = myatan2(pieceptr[N]->c, pieceptr[N]->a);
  2629.       pieceptr[N]->r2 = myatan2(-pieceptr[N]->b, pieceptr[N]->d);
  2630.       pieceptr[N]->det = fabs(pieceptr[N]->a * pieceptr[N]->d -
  2631.                                          pieceptr[N]->b * pieceptr[N]->c);
  2632.       if (pieceptr[N]->det == 0.0)
  2633.         pieceptr[N]->det = 0.01;
  2634.       pieceptr[N]->dens = pieceptr[N]->p / pieceptr[N]->det;
  2635.  
  2636.       /*** Compute new Box corners from new coeffs */
  2637.       pieceptr[N]->boxo.x = pieceptr[N]->e * GZZWIDTH;
  2638.       pieceptr[N]->boxo.y = pieceptr[N]->f * GZZWIDTH; /* sic! */
  2639.       pieceptr[N]->boxx.x = (pieceptr[N]->a + pieceptr[N]->e) * GZZWIDTH;
  2640.       pieceptr[N]->boxx.y = (pieceptr[N]->c + pieceptr[N]->f) * GZZWIDTH;
  2641.       pieceptr[N]->boxy.x = (pieceptr[N]->b * GZZHEIGHT/GZZWIDTH +
  2642.          pieceptr[N]->e) * GZZWIDTH;
  2643.       pieceptr[N]->boxy.y = (pieceptr[N]->d * GZZHEIGHT/GZZWIDTH +
  2644.          pieceptr[N]->f) * GZZWIDTH;
  2645.       pieceptr[N]->boxz.x = pieceptr[N]->boxy.x + pieceptr[N]->boxx.x -
  2646.          pieceptr[N]->boxo.x;
  2647.       pieceptr[N]->boxz.y = pieceptr[N]->boxy.y + pieceptr[N]->boxx.y -
  2648.          pieceptr[N]->boxo.y;
  2649.     }
  2650.  
  2651.     else    /* scanf failed to match 7 doubles on line - skip it! */
  2652.       FreePiece(pieceptr[N--]);
  2653.  
  2654.   }    /* End while loop on lines */
  2655.  
  2656.   selpiece = N;
  2657.  
  2658.   if (N == MAX_N && !eofread && getc(fp) != EOF)  /* Exited loop at N==MAX_N */
  2659.     ShowError("File too long; IFS may be truncated");
  2660.  
  2661.   if (N == -1) {   /* Not a single transformation was found! */
  2662.     ShowError("No IFS found in File - Aborting Load!");
  2663.     fclose(fp);
  2664.     return(0);
  2665.   }
  2666.  
  2667.   /*** If probabilities read from file were cumulative, convert to non-cum */
  2668.   if (pieceptr[N]->p == 1.000) {
  2669.     for (i = N; i > 0; i--) {
  2670.       pieceptr[i]->p = pieceptr[i]->p - pieceptr[i-1]->p;
  2671.       pieceptr[i]->dens = pieceptr[i]->p / pieceptr[i]->det;
  2672.     }
  2673.   }
  2674.  
  2675.   /*** Normalize Piece densities so smallest one is 1.000 */
  2676.   mindens = 1000000.;
  2677.   for (i = 0; i <= N; i++)
  2678.     if (pieceptr[i]->dens < mindens)
  2679.       mindens = pieceptr[i]->dens;
  2680.   for (i = 0; i <= N; i++)
  2681.     pieceptr[i]->dens /= mindens;
  2682.  
  2683.   ComputeProbs(); /* Adjust all probabilities in case rounding errors in */
  2684.                   /* file made their sum != 1.000 */
  2685.  
  2686.   fclose(fp);         /* close disk file */
  2687.  
  2688.   /* Transform loaded IFS to fit in window if it exceeds it, */
  2689.   /* and generate Collage */
  2690.   Optimize(0);
  2691.  
  2692.   return(1);
  2693. }
  2694. /*===========================================================================*/
  2695. int Optimize(mode) /* Creates an Outline from current Attractor, creates    */
  2696. int mode;          /* a Collage from it and displays it. If mode==1, also   */
  2697.                    /* modifies the IFS so its attractor fills the window.   */
  2698.                    /* If mode==0, does this only if current Attractor would */
  2699.                    /* overflow the window.                                  */
  2700.                    /* Returns 1 if successful and 0 if it failed (because   */
  2701. {                  /* not in Collage Editor) and no modifications were done.*/
  2702.   long xmin, xmax, ymin, ymax;  /* GZZ PIXEL coords of old boundary rect */
  2703.   int px1, py1; /* GZZ PIXEL coords  of Topleft corner of new boundary rect */
  2704.   double x0,y0; /* REAL plane coords of Topleft corner of old boundary rect */
  2705.   double x1,y1; /* REAL plane coords of Topleft corner of new boundary rect */
  2706.   double m;     /* Magnification to apply to attractor so it fills window */
  2707.   double attrw, attrh;  /* PIXEL Width & Height of old boundary rect */
  2708.   struct piece tmppiece;  /* will be used to hold temporary trans' coeffs */
  2709.   int j;
  2710.  
  2711.   if (drawmode != VECTOR) {     /* Abort if not in Collage Editor! */
  2712.     ShowError("No IFS!");
  2713.     return(0);
  2714.   }
  2715.  
  2716.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  2717.  
  2718.   SetRast(r, 0L);  /* Erase  Old Outline and Collage */
  2719.  
  2720.   FindBoundary(&xmin, &xmax, &ymin, &ymax); /* Identify old boundary rect */
  2721.  
  2722.   /*** If mode is 1, or mode is 0 but attractor exceeds window, modify IFS ***/
  2723.  
  2724.   if (mode==1 || xmin<0 || xmax >= GZZWIDTH || ymin<0 || ymax >= GZZHEIGHT) {
  2725.     /*** Compute required magnification and relocation of attractor */
  2726.  
  2727.     attrw = (double)((xmax - xmin) + 1);
  2728.     attrh = (double)((ymax - ymin) + 1);
  2729.  
  2730.     if ((double)attrw/(double)attrh >= (double)GZZWIDTH/(double)GZZHEIGHT) {
  2731.       m = 0.95 * (double)GZZWIDTH / attrw;
  2732.       px1 = 0.025 * (double)GZZWIDTH;
  2733.       py1 = 0.025 * (double)GZZHEIGHT +
  2734.                            (0.95 * (double)GZZHEIGHT - m * attrh) / 2.0;
  2735.     }
  2736.     else {
  2737.       m = 0.95 * (double)GZZHEIGHT / attrh;
  2738.       px1 = 0.025 * (double)GZZWIDTH +
  2739.                            (0.95 * (double)GZZWIDTH - m * attrw) / 2.0;
  2740.       py1 = 0.025 * (double)GZZHEIGHT;
  2741.     }
  2742.     x0 = (double)xmin / (double)GZZWIDTH;
  2743.     y0 = (double)ymin / (double)GZZWIDTH;
  2744.     x1 = (double)px1 / (double)GZZWIDTH;
  2745.     y1 = (double)py1 / (double)GZZWIDTH;
  2746.  
  2747.     /*** Modify all piece coefficients to make new attractor fill window */
  2748.  
  2749.     for (i = 0; i <= N; i++) {   /* Loop on pieces */
  2750.       pieceptr[i]->e = m * pieceptr[i]->e + 
  2751.        (pieceptr[i]->a - 1) * (m * x0 - x1) + pieceptr[i]->b * (m * y0 - y1);
  2752.       pieceptr[i]->f = m * pieceptr[i]->f + 
  2753.        pieceptr[i]->c * (m * x0 - x1) + (pieceptr[i]->d - 1) * (m * y0 - y1);
  2754.  
  2755.       /*** Recompute new Box corners from new coeffs */
  2756.       pieceptr[i]->boxo.x = pieceptr[i]->e * GZZWIDTH;
  2757.       pieceptr[i]->boxo.y = pieceptr[i]->f * GZZWIDTH; /* sic! */
  2758.       pieceptr[i]->boxx.x = (pieceptr[i]->a + pieceptr[i]->e) * GZZWIDTH;
  2759.       pieceptr[i]->boxx.y = (pieceptr[i]->c + pieceptr[i]->f) * GZZWIDTH;
  2760.       pieceptr[i]->boxy.x = (pieceptr[i]->b * GZZHEIGHT/GZZWIDTH +
  2761.          pieceptr[i]->e) * GZZWIDTH;
  2762.       pieceptr[i]->boxy.y = (pieceptr[i]->d * GZZHEIGHT/GZZWIDTH +
  2763.          pieceptr[i]->f) * GZZWIDTH;
  2764.       pieceptr[i]->boxz.x = pieceptr[i]->boxy.x + pieceptr[i]->boxx.x -
  2765.          pieceptr[i]->boxo.x;
  2766.       pieceptr[i]->boxz.y = pieceptr[i]->boxy.y + pieceptr[i]->boxx.y -
  2767.        pieceptr[i]->boxo.y;
  2768.     }    /* End loop on pieces */
  2769.   }    /* End if mode==1... */
  2770.  
  2771.   RenderImage(1);   /* Render new attractor in OUTLINE Bitplane */
  2772.   CopyMem((char*)Window->RPort->BitMap->Planes[1], 
  2773.               (char*)outlinebufptr, (long)RASSIZE(WIDTH,HEIGHT));
  2774.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2775.  
  2776.   /*** Transform the new Outline (=attractor) into all pieces' piecemaps */
  2777.   /*** Draw each piece into the collage as you go */
  2778.  
  2779.   selpiece = N;
  2780.   for (i = 0; i <= N-1; i++) {  /* Loop on deselected pieces */
  2781.  
  2782.     TransformPiece(outlinebufptr, pieceptr[i]->piecemap, pieceptr[i]);
  2783.  
  2784.     /* Blit this piece from its piecemap to plane 0 */
  2785.     SetWrMsk(r,0x0001);  /* Write protect all planes except plane 0 */
  2786.     SetAPen(r, 1L);
  2787.     BltTemplate((char*)pieceptr[i]->piecemap,(long)Window->BorderLeft,
  2788.         (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT);
  2789.   }
  2790.  
  2791.   /* Now transform Selected Piece & Blit it from piecemap to plane 2 */
  2792.   TransformPiece(outlinebufptr, pieceptr[i]->piecemap, pieceptr[i]);
  2793.   SetWrMsk(r,0x0004);  /* Write protect all planes except plane 2 */
  2794.   SetAPen(r, 4L);
  2795.   BltTemplate((char*)pieceptr[selpiece]->piecemap,(long)Window->BorderLeft,
  2796.    (long)WIDTH/8, r, 0L, 0L, (long)GZZWIDTH, (long)GZZHEIGHT);
  2797.   
  2798.   DrawBox();  /* Draw Vector Box of Selected Piece */
  2799.   /* Note that Rastport's WrMask is restored within DrawBox() */
  2800.  
  2801.   return(1);
  2802. }
  2803. /*===========================================================================*/
  2804.  
  2805. RenderImage(z) /* If z == 0, Render the Attractor in Image Screen at current */
  2806. int z;   /*  resolution & depth settings; stop and return on click in Image */
  2807.          /* screen. If z == 1, render it in Outline bitplane. */
  2808.          /* Probabilities and coefficients are scaled to integers so can use */
  2809.          /* faster integer math */
  2810. {
  2811.   struct Message * GetMsg();
  2812.   struct IntuiMessage *message;
  2813.   int randprob;   /* Random probability value */
  2814.   double sum;        /* cum probability */
  2815.   int k;
  2816.   long unitwidth, unitheight;
  2817.     /* width, height in image screen pixels of unit square in the plane */
  2818.   long iter; /* Iteration counter */
  2819.   struct RastPort *imagerp;  /* Pointer to rastport of Image window */
  2820.   long pix;  /* Color (Pen #) of a pixel */
  2821.   long ReadPixel();
  2822.   long ai[MAX_N+1], bi[MAX_N+1], ci[MAX_N+1],
  2823.        di[MAX_N+1], ei[MAX_N+1], fi[MAX_N+1]; /* Scaled integer coefficients */
  2824.   long xi, yi, tmpxi; /* Scaled integer pixel coordinates */
  2825.   int cumprobi[MAX_N+2];   /* Scaled integer Cum Probabilities */
  2826.      /* cumprobi array has MAX_N+2 elements because using MAX_N+1 caused a */
  2827.      /* Crash for unclear reasons ?!?! */
  2828.  
  2829.     /*** Prepare stuff for iteration loop ***/
  2830.  
  2831.   if (z == 0) {     /* Rendering is in Image Screen */
  2832.     /*** Open Image Window in its screen (Closing any previous image screen) */
  2833.     if (exists_image)
  2834.       CloseImageScreen();
  2835.     OpenImageScreen(renderwidth, renderheight, renderdepth);
  2836.                               /* OpenImageScreen() also opens ImageWindow */
  2837.     imagerp = ImageWindow->RPort; 
  2838.     SetAPen(imagerp,2L); /* Will be modified in loop for Greyscale rendering */
  2839.     SetDrMd(imagerp, JAM1);
  2840.  
  2841.     SetWindowTitles(ImageWindow, -1L,
  2842.                                (char *)" Click in screen to stop rendering");
  2843.  
  2844.     unitwidth = GZZWIDTH*(renderwidth/320); /* GZZWIDTH is of Collage window!*/
  2845.     unitheight = GZZWIDTH * (renderheight/200);
  2846.  
  2847.   }
  2848.   else {   /* Rendering in Outline plane */
  2849.     SetWrMsk(r, 0x0002);  /* Write protect all planes except plane 1 */
  2850.     SetAPen(r, 2L);
  2851.     SetDrMd(r, JAM1);
  2852.     
  2853.     unitwidth = unitheight = GZZWIDTH;
  2854.   }
  2855.  
  2856.   srand((unsigned int)(time(NULL)%10000L));  /* seed random gen from sys time*/
  2857.   
  2858.   sum = 0;
  2859.   for (k = 0; k <= N; k++) {          /* Scale Coefficients as longs */
  2860.     ai[k] = (long)(pieceptr[k]->a * 1000000);
  2861.     bi[k] = (long)(pieceptr[k]->b * 1000000);
  2862.     ci[k] = (long)(pieceptr[k]->c * 1000000);
  2863.     di[k] = (long)(pieceptr[k]->d * 1000000);
  2864.     ei[k] = (long)(pieceptr[k]->e * 1000000 * unitwidth);
  2865.     fi[k] = (long)(pieceptr[k]->f * 1000000 * unitwidth);
  2866.  
  2867.     sum = sum + pieceptr[k]->p;  /* Compute scaled Cum Probabilities */
  2868.     cumprobi[k] = (int)(sum * (double)RAND_MAX);
  2869.   }
  2870.  
  2871.   xi = unitwidth / 3;   /* Initial coordinates */
  2872.   yi = unitwidth / 3;
  2873.  
  2874.     /*** Iterate until user clicks in Image window or until 1000 points ***/
  2875.     /*** (depending on z) ***/
  2876.  
  2877.   for (iter = 0; 1; iter++) {         /* endless loop */
  2878.  
  2879.     /*** Select a Piece at random */
  2880.     randprob = rand();
  2881.                  /* rand() returns a random # (between 0 - RAND_MAX) */
  2882.     for (k = 0; cumprobi[k] < randprob; k++) {
  2883.       /* the following IF is to trap problem case when randprob = RAND_MAX */
  2884.       /* exactly and cum probability is RAND_MAX minus slight delta due to */
  2885.       /* rounding errors */
  2886.       if (k > N) {
  2887.         k--;
  2888.         break;
  2889.       }
  2890.     }
  2891.  
  2892.     /* Apply transformation #k to previous (x,y) */
  2893.     tmpxi = (ai[k] * xi + bi[k] * yi + ei[k]) / 1000000;
  2894.     yi = (ci[k] * xi + di[k] * yi + fi[k]) / 1000000;
  2895.     xi = tmpxi;
  2896.  
  2897.     if (iter > 19) {    /* Render the pixel (except 1st 20 points) */
  2898.  
  2899.       if (z == 0) {   /*** draw in Image Screen, quit on mouseclick in it */
  2900.  
  2901.         if (renderdepth > 1) {  /* Grayscale rendering */
  2902.           pix = ReadPixel(imagerp, xi, yi * unitheight / unitwidth);
  2903.           if (++pix < (1<<renderdepth)) {
  2904.             SetAPen(imagerp, pix);
  2905.             WritePixel(imagerp, xi, yi * unitheight / unitwidth);
  2906.           }
  2907.         }
  2908.         else    /* B&W rendering - A Pen was set outside main loop */
  2909.           WritePixel(imagerp, xi, yi * unitheight / unitwidth);
  2910.  
  2911.         /*** Quit rendering loop if user clicked in ImageWindow */
  2912.         if (message = (struct IntuiMessage *)GetMsg(ImageWindow->UserPort)) {
  2913.           ReplyMsg(message);
  2914.           if (message->Code == SELECTUP) {
  2915.             SetWindowTitles(ImageWindow,
  2916.                      (char *)" Click in screen to push it back", -1L);
  2917.             ShowTitle(ImageScreen, FALSE);  /* Hide screen depth gadgets! */
  2918.             break;  /* From for iter */
  2919.           }
  2920.         }
  2921.       }
  2922.  
  2923.       else {     /* z == 1, draw in outline plane, quit after 1000 iters */
  2924.  
  2925.         WritePixel(r, xi, yi);
  2926.         if (iter > 999)
  2927.           break;  /* From for iter */
  2928.       }
  2929.     }  /* End if iter > 19 */
  2930.   }  /* End for iter */
  2931.  
  2932.   if (z == 0) {  /*** Wait for user click in Image screen to push it to back */
  2933.     while (!(message = (struct IntuiMessage*)GetMsg(ImageWindow->UserPort)))
  2934.       ;       /* Wait for a message */
  2935.     ReplyMsg(message);
  2936.     SetWindowTitles(ImageWindow,
  2937.                (char *)" ", (char *)" Click in screen to push it back");
  2938.     ShowTitle(ImageScreen, TRUE);  /* OK to have depth gadgets now */
  2939.     ScreenToBack(ImageScreen);
  2940.     MoveScreen(ImageScreen, 0L, (long)(-1*ImageScreen->TopEdge));
  2941.     ScreenToFront(Screen);
  2942.     ActivateWindow(Window);
  2943.   }
  2944.   else  /* z == 1, unprotect Outline window planes */
  2945.     SetWrMsk(r, 0xFFFF);
  2946. }
  2947. /*===========================================================================*/
  2948.  
  2949. SaveILBM()    /* Saves the current Image to an IFF disk file */
  2950. {
  2951.   LONG file;  /* File handle [we use AmigaDOS Open(), not Manx open()] */
  2952.   UBYTE *savebuffer;
  2953.   char *fnam;
  2954.  
  2955.   if (exists_image == 0) {
  2956.     ShowError("No Image to save!");
  2957.     return;
  2958.   }
  2959.  
  2960.   fnam = GetFileName("Save to File:");  /* Get the filename */
  2961.   if (*fnam == '\0') /* User selected CANCEL or gave no filename */
  2962.     return;
  2963.  
  2964.   if ((file = Open(fnam, MODE_NEWFILE)) == NULL) {
  2965.     ShowError("Can't open file!");
  2966.     return;
  2967.   }
  2968.   if ((savebuffer = (UBYTE *)AllocMem(8000L, MEMF_CHIP|MEMF_PUBLIC)) == NULL) {
  2969.     ShowError("Can't allocate Save Buffer Memory!");
  2970.     return;
  2971.   }
  2972.  
  2973.   /* Use EA IFF routine PutPict() to save the image to file */
  2974.   ModifyMousePtr(Window, 1);   /* Bring up 'ZZ' pointer */
  2975.   SetWindowTitles(ImageWindow,-1L,
  2976.                               (char *)"");
  2977.   ShowTitle(ImageScreen, FALSE);  /* Hide screen depth gadgets! */
  2978.   ActivateWindow(ImageWindow); /* To make title change take effect */
  2979.   ActivateWindow(Window);
  2980.   PutPict(file, ImageWindow->RPort->BitMap, ImageScreen->Width, ImageScreen->Height,
  2981.    (WORD *)(&(ImageScreen->ViewPort))->ColorMap->ColorTable, savebuffer,8000L);
  2982.   SetWindowTitles(ImageWindow,-1L,
  2983.                               (char *)" Click in screen to push it back");
  2984.   ShowTitle(ImageScreen, TRUE);  /* OK to have depth gadgets now */
  2985.   ActivateWindow(ImageWindow); /* To make title change take effect */
  2986.   ActivateWindow(Window);
  2987.   FreeMem(savebuffer, 8000L);
  2988.   Close(file);
  2989.   ModifyMousePtr(Window, -1);   /* Remove 'ZZ' pointer */
  2990. }
  2991. /*===========================================================================*/
  2992.  
  2993. struct piece *AllocPiece()     /* Allocate memory for a piece structure and */
  2994. {                    /* its piecemap. Return pointer to the piece structure */
  2995.                      /* Or NULL if can't allocate memory */
  2996.   struct piece *tmppieceptr;
  2997.   UBYTE *mapptr;
  2998.  
  2999.   /* Allocate memory for piece structure */
  3000.   if ((tmppieceptr = 
  3001.       (struct piece *)AllocMem((long)sizeof(struct piece), NULL)) == NULL) {
  3002.     ShowError("Can't allocate Memory!");
  3003.     return(NULL);
  3004.   }
  3005.   /* Allocate piecemap CHIP RAM */
  3006.   if ((mapptr = (UBYTE*)AllocRaster((long)WIDTH, (long)GZZHEIGHT)) == NULL) {
  3007.     FreeMem((void*)tmppieceptr, (long)sizeof(struct piece));
  3008.     ShowError("Can't allocate Memory!");
  3009.     return(NULL);
  3010.   }
  3011.   /* Clear piecemap */
  3012.   BltClear((char*)mapptr, (long)RASSIZE(WIDTH,GZZHEIGHT), 1L);
  3013.   tmppieceptr->piecemap = mapptr;   /* Link bitplane to piece structure */
  3014.   return(tmppieceptr);
  3015. }
  3016. /*===========================================================================*/
  3017.  
  3018. FreePiece(piecepntr)          /* Frees the memory used for a piece structure */
  3019. struct piece *piecepntr;      /* and its piecemap. */
  3020. {
  3021.   /* Deallocate CHIP RAM of piecemap bitplane */
  3022.   FreeRaster((void*)(piecepntr->piecemap), (long)WIDTH, (long)GZZHEIGHT);
  3023.  
  3024.   /* Deallocate memory of piece structure */
  3025.   FreeMem((void*)piecepntr, (long)sizeof(struct piece));
  3026. }
  3027. /*===========================================================================*/
  3028.  
  3029. p(text)      /* screen text output routine, similar to BASIC's PRINT */
  3030.              /* puts text in consecutive lines in rastport pointed at by the */
  3031.              /* external pointer Textrp. Line position is maintained between */
  3032.              /* calls, but is reset to top if text==NULL. No overflow check */
  3033. char *text;  /* is done -- text should fit in rastport area! */
  3034. {
  3035.   static struct IntuiText Textline = {
  3036.     1, 0, JAM1,        /* FrontPen, BackPen, DrawMode */
  3037.     0, NULL,            /* LeftEdge, TopEdge */
  3038.     NULL,              /* ITextFont */
  3039.     NULL, NULL            /* IText, NextText */
  3040.   };
  3041.   static int line = 0; /* vert. position of line to be printed next (0 - 24) */
  3042.  
  3043.   if (text == NULL)   /* reset to top line of rastport */
  3044.     line = 0;
  3045.   else {
  3046.     Textline.TopEdge = 8 * line;   /* Put text into line on screen */
  3047.     Textline.IText = (UBYTE*)text;
  3048.     PrintIText(Textrp, &Textline, 0L, 0L);
  3049.     line++;  /* increment line for next call */
  3050.   }
  3051. }
  3052. /*===========================================================================*/
  3053.  
  3054. FindBoundary(xminp, xmaxp, yminp, ymaxp)     /* Iterates IFS without drawing */
  3055.               /* and returns PIXEL coords of boundary rectangle of attractor */
  3056.               /* in variables pointed at by its arguments */ 
  3057.               /* Uses float math, not scaled int, to avoid overflow on very */
  3058.               /* large coeffs of loaded IFS files from other programs */
  3059. long *xminp, *xmaxp, *yminp, *ymaxp;   /* Pointers! */
  3060. {
  3061.   int randprob;   /* Random probability value */
  3062.   double sum;        /* cum probability */
  3063.   int k;
  3064.   long iter; /* Iteration counter */
  3065.   double xd, yd, tmpxd; /* Real pixel coordinates */
  3066.   double xdmin, ydmin, xdmax, ydmax; /* Real boundary rect coords */
  3067.   int cumprobi[MAX_N+2];   /* Scaled integer Cum Probabilities */
  3068.     /* cumprobi array has MAX_N+2 elements because using MAX_N+1 caused a */
  3069.     /* Crash for unclear reasons in RenderImage() ?!?! */
  3070.  
  3071.   /*** Prepare stuff for iteration loop ***/
  3072.  
  3073.   srand((unsigned int)(time(NULL)%10000L));  /* seed random gen from sys time*/
  3074.  
  3075.   sum = 0;
  3076.   for (k = 0; k <= N; k++) {          /* Scale Coefficients as longs */
  3077.     sum = sum + pieceptr[k]->p;  /* Compute scaled Cum Probabilities */
  3078.     cumprobi[k] = (int)(sum * (double)RAND_MAX);
  3079.   }
  3080.  
  3081.   xdmin = 1000000000.;     /* Initialize boundary rectangle coords */
  3082.   xdmax = -1000000000.;
  3083.   ydmin = 1000000000.;
  3084.   ydmax = -1000000000.;
  3085.  
  3086.   xd = 0.3;   /* Initial coordinates */
  3087.   yd = 0.3;
  3088.  
  3089.   /*** Iterate 1000 times without drawing ***/
  3090.  
  3091.   for (iter = 0; iter < 1000; iter++) {
  3092.  
  3093.     /*** Select a Piece at random */
  3094.     randprob = rand();
  3095.                  /* rand() returns a random # (between 0 - RAND_MAX) */
  3096.     for (k = 0; cumprobi[k] < randprob; k++) {
  3097.       /* the following IF is to trap problem case when randprob = RAND_MAX */
  3098.       /* exactly and cum probability is RAND_MAX minus slight delta due to */
  3099.       /* rounding errors */
  3100.       if (k > N) {
  3101.         k--;
  3102.         break;
  3103.       }
  3104.     }
  3105.  
  3106.     /* Apply transformation #k to previous (x,y) */
  3107.     tmpxd = pieceptr[k]->a * xd + pieceptr[k]->b * yd + pieceptr[k]->e;
  3108.     yd = pieceptr[k]->c * xd + pieceptr[k]->d * yd + pieceptr[k]->f;
  3109.     xd = tmpxd;
  3110.  
  3111.     if (iter > 19) {    /* Adjust rectangle  (except 1st 20 points) */
  3112.       if (xd < xdmin)
  3113.         xdmin = xd;
  3114.       if (xd > xdmax)
  3115.         xdmax = xd;
  3116.       if (yd < ydmin)
  3117.         ydmin = yd;
  3118.       if (yd > ydmax)
  3119.         ydmax = yd;
  3120.     }
  3121.  
  3122.   }  /* End for iter */
  3123.  
  3124.   /***  Convert real coords to pixel coords for returned values */
  3125.  
  3126.   *xminp = (long)(xdmin * GZZWIDTH);
  3127.   *xmaxp = (long)(xdmax * GZZWIDTH);
  3128.   *yminp = (long)(ydmin * GZZWIDTH);
  3129.   *ymaxp = (long)(ydmax * GZZWIDTH);
  3130. }
  3131. /*===========================================================================*/
  3132.